3

I have two arrays @uarts and @txd which look like this

@uarts = qw(uart_1 uart_10 uart_3 uart_9 ); 
@txd = qw(PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10 PIO_uart_5 PIO_uart_9 PIO_uart_7);

I want to extract only those elements from @txd which contains any of the elements of @uarts. The code I have written for it is as follows but is not working.

my @array;
for (my $i = 0 ; $i <= $#uarts ; $i++) {
  @array = grep { $_ =~ /$uarts[$i]/ } (@txd);
  print "@array\n";
}
3
  • Please learn to format your Perl code so that it is readable. I have done it for you in this case, but it will benefit people trying to help you as well as yourself if you post intelligible code. Commented Mar 20, 2015 at 11:24
  • I did the indentation this time. Sorry but I am not able to understand Commented Mar 20, 2015 at 11:36
  • Ah okay. Well it is certainly better than before but you have an unusual formatting style with the braces indented by one level and the contents indented again. It would also have been nice to see a space after for to distinguish it from a function call. Take a look at perldoc perlstyle for the most common standards Commented Mar 20, 2015 at 11:47

4 Answers 4

5

I've got to be honest. I think both map and grep are annoying enough to understand that if you're unfamiliar with perl - steer clear. They're not gaining you much - they look like they're reducing the complexity of your code, but that's because the loop of a grep is implicit. So all you're doing is making your code harder to grok.

Also - I really dislike that style of for loop - it's almost always redundant in perl - in the case above, the only element you're referencing is the current one (It's a different matter if you're accessing 'next' or 'previous' elements).

So unroll it thus:

foreach my $uart ( @uarts ) {
    foreach my $PIO ( @txd ) {
        if ( $PIO =~ m/$uart/ ) { 
            print "$PIO matches $uart\n";
        }
     }
 }

NB: This doesn't do any sort of uniqueness testing so if multiple matches occur, you'll get dupes.

Oh, and turn on use strict; and use warnings;. Your array declarations are wrong.

my @uarts = qw ( uart_1 uart_10 uart_3 uart_9 );
my @txd   = qw ( PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10
                 PIO_uart_5 PIO_uart_9PIO_uart_7
);

I would also point out - you've scoped @array outside your loop, which presumably means wanting to keep it. But then you clobber it each iteration by assigning the output of grep.

I would suggest you either scope @array within the loop, or look at push/pop and shift/unshift as ways to add and remove elements from an existing array.

Sign up to request clarification or add additional context in comments.

3 Comments

If you need next/previous elements then something like foreach my $i ( 0 .. $#my_array ) is often easier to read than a traditional C-style for loop
Actually I've "use strict;" and "use warnings;" on already and these arrays uarts and txd I haven't declared here. These are there in my code in previous lines of script. I gave just a small chunk of the code starting from "my @array " .
And for the code you suggested I want to print the matched elements of @txd
3

Your code "works" as it stands. You should always say what you mean by "not working".

What I think is the problem is that your code is working exactly as you describe. It finds "those elements from @txd which contains any of the elements of @uarts" whereas I think you need those elements that end with any of the strings in @uarts.

As it stands your program outputs

PIO_uart_1 PIO_2_uart_1 PIO_uart_10
PIO_uart_10
PIO_uart_3
PIO_uart_9

so when checking for uart_1 it is finding PIO_uart_10 because the former is a substring of the latter. To look for elements that end with a given uart string you just need to add an end-of-line anchor to the regex so that it becomes

@array = grep { $_ =~ /$uarts[$i]$/ } (@txd)

That changes the output to

PIO_uart_1 PIO_2_uart_1
PIO_uart_10
PIO_uart_3
PIO_uart_9

which I hope is what you want?

But it could be written a little better. It is best to loop over the contents of an array unless you specifically need the index, and there is no need for @array to be a global variable (and it could be anmed a lot better) so this will work for you

use strict;
use warnings;

my @uarts = qw(uart_1 uart_10 uart_3 uart_9 ); 
my @txd = qw(PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10 PIO_uart_5 PIO_uart_9 PIO_uart_7);

for my $uart ( @uarts ) {
  my @matches = grep /$uart\z/, @txd;
  print "@matches\n";
}

output

PIO_uart_1 PIO_2_uart_1
PIO_uart_10
PIO_uart_3
PIO_uart_9

1 Comment

Exactly I just had to use $ in the end .. Thankyou :)
1

You could fix your code by just changing @array = grep{$_=~ /$uarts[$i]/}(@txd); into push @array, grep{$_=~ /$uarts[$i]/}(@txd);.

But sane and efficient way how to do it is prepare matching regexp once and do it in O(N+M) instead of O(N*M).

use strict;
use warnings;

my @uarts = qw(uart_1 uart_10 uart_3 uart_9);
my @txd
    = qw(PIO_uart_1 PIO_2_uart_1 PIO_uart_3 PIO_uart_10 PIO_uart_5 PIO_uart_9 PIO_uart_7);

my @array = do {
    my $re = join '|', map quotemeta, @uarts;
    $re = qr/$re/;
    grep /$re/, @txd;
};

print "@array\n";

2 Comments

I really doubt that that's true: your answers often have issues. This one, for instance, has the same problem as the OP's original code: that it finds elements of @txd that contain any of the strings in @uarts instead of those that end with any of them. There's also no point in compiling the regex for a one-time use, as the grep line will do that anyway. I long ago gave up the chase for votes—that way lies insanity!
You are right the $re = qr/$re/; line is unnecessary, but it is just habit to explicitly compile regexp. I like to see in the code this regexp is constant since then. I usually use same regexp at various places.
-3
@uarts =(uart_1,uart_10,uart_3,uart_9 ); 
@txd =(PIO_uart_1,PIO_2_uart_1,PIO_uart_3,PIO_uart_10,PIO_uart_5,PIO_uart_9,PIO_uart_7);
my @array;
for(my $i=0;$i<=$#uarts;$i++ )
{
    @array=grep{$_=~/$uarts[$i]/}(@uarts);
    print "@array\n";
}

output

uart_1 uart_10
uart_10
uart_3
uart_9

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.