cmv has asked for the wisdom of the Perl Monks concerning the following question:

Update: Many thanks to all for tutoring me on map! I really enjoy learning things quickly from experts.

My favorite solution is Re: Scanning a list for indices - Thanks Fletch++.

What a wonderful language that lets you do things like:

@a1_index{ @a1 } = 0..$#a1;
Folks-

I'm sorry if this has been answered already (I gotta believe it has), but I'm not finding anything with my searches (which I'm admittedly bad at). Any help and all patience is appreciated!

The short version of my question is this. Given 2 lists:

my @a1 = ( qw(one two three four five six seven eight nine ten) ); my @a2 = ( qw(four seven nine) );

How do I generate a third list:

@indices = ( qw( 3 6 8) );
Which contains the indices of where all elements from @a2 show up in @a1? The attached code is my solution, but my bones tell me that there must be a better one. Any help is much appreciated!

-Craig

The long version of my question is that I have an item to add to a listbox that must be kept sorted, and some of the items are highlighted. My current design is to get the current list from the listbox into an array, push the new item, resort, and then slam the entire newly sorted list back into the listbox (I'm open to better options here if this isn't the best).

I also have another list of currently highlighted listbox items (backgrounds are some other color besides the default). As soon as I slam in the newly sorted list, all highlighting in the listbox is (obviously) removed. In order to replace the highlighting I need to find the positional index of each highlighted item in the newly sorted list so I can do...

$listbox->itemconfigure($index, -background=>'highlightyellow');

...since itemconfigure needs the positional index of the thing I want (listbox always frustrates me this way). I suspect a better answer is to use tied variables with the listbox, but I'm having trouble getting my brain around how those work.

use strict; use warnings; use Data::Dumper; my @a1 = ( qw(one two three four five six seven eight nine ten) ); my @a2 = ( qw(four seven nine) ); my @indices; my $c=0; map { foreach my $i (@a2) { if($_ eq $i) {push(@indices, $c)}; } $c++; } @a1; print "indices DUMP:\n", Dumper(\@indices), "\n";

Replies are listed 'Best First'.
Re: Scanning a list for indices
by Fletch (Bishop) on Feb 13, 2008 at 15:10 UTC

    Presuming elements of @a1 are unique . . .

    my %a1_indexen; @a1_index{ @a1 } = 0..$#a1; my @indexen = map $a1_index{ $_ }, @a2;

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Scanning a list for indices
by jettero (Monsignor) on Feb 13, 2008 at 15:00 UTC
    Why use map without using it's return value? why not two for loops? merlyn doesn't like that, and I agree with him.
    # for loop for my $i (0 .. $#a1) { for my $j (0 .. $#a2) { push @indices, $i if $a2[$j] eq $a1[$i]; } }
    # schwartzian [sp?] transform (perhaps not a paradigmatic example thou +gh) my @indicies = map {$_->[0]} grep {$a1[$_->[0]] eq $a2[$_->[1]]} map { my $l = $_; (map {[$l,$_]} 0 .. $#a2) } 0 .. $#a2;

    -Paul

      Paul-

      Why use map without using it's return value? why not two for loops?

      Well, I'm just now maturing enough with perl to begin to understand how to think about things with map (look ma, I'm using map!), so naturally I want to show off a bit. However, I also thought map might be more efficient here than 2 for loops (is that correct?).

      I couldn't figure out how to use the return value from map to get done what I want. Any pointers here would be great!

      Thanks!

      -Craig Update: Oh, you just did... Thanks!

        Nope, worse. The map still loops just like a for loop would have to, but it also has to manipulate an argument stack (or something like it) so that it can set the $_ and then return it. I'm really not very sure about what I just said, but it is my understanding that is the case.

        The only way to know for sure it so try it.

        use strict; use Benchmark qw(cmpthese); my @a1 = (1 .. 4_000); cmpthese(4_000, { for_loops => sub { for(@a1) { $_ = $_ + 1 } }, map_loops => sub { map { $_ = $_ + 1 } @a1 }, });

        -Paul

Re: Scanning a list for indices
by jwkrahn (Abbot) on Feb 13, 2008 at 15:08 UTC
    my @indices = grep { my $i = $_; grep $a1[ $i ] eq $_, @a2 } 0 .. $#a1 +;