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

Dear Monks,

Is there a nicer way to find the index number matching a string in an array rather than doing foreach and going through every line?

I know grep can be used to find is the string is there, but it does not give the index number.

Thank you.

Replies are listed 'Best First'.
Re: finding index number in an array
by tlm (Prior) on May 27, 2005 at 22:12 UTC

    The first thing that comes to mind, in general:

    use List::Util 'first'; my $first_index = first { test( $arr[ $_ ] ) } 0..$#arr;
    This puts the index of the first member of @arr for which test returns true. test can be anything, including a pattern match, e.g.
    my $first_index = first { $arr[ $_ ] =~ /$pat/ } 0..$#arr;

    BTW, you can achieve the same effect with grep, although less efficiently, because every element of the array will be tested:

    my $first_index = ( grep test( $arr [$_ ] ), 0..$#arr )[ 0 ];

    Update: I found BrowserUk's reply puzzling at first, but then I realized that we probably interpreted the OP's "nicer" in different ways. Let me then clarify that the solutions I proposed above all use what amounts to a foreach loop "under the hood," so they are "nicer" (maybe) only in a syntatic, not algorithmic, sense. In general, as BrowserUk asserts, there is no avoiding this sequential search (although by using first instead of grep the search is as efficient as possible).

    the lowliest monk

Re: finding index number in an array
by mrborisguy (Hermit) on May 27, 2005 at 22:12 UTC

    This isn't really an answer to your question specifically, but it's a possible workaround.

    my @array = qw/some stuff in here/; #instead of: my @matches = grep { /^s/ } @array; #which would give you ("some","stuff"); #you could try something like this: my @indices = grep { $array[ $_ ] =~ /^s/ } 0..$#array; #which would give you (0,1), which are the indices of the array you + want. #and then to get matches, you can still do something like: my @matches = @array[ @indices ];

        -Bryan

Re: finding index number in an array
by BrowserUk (Patriarch) on May 27, 2005 at 22:13 UTC

    Basically no!

    But depending upon what your actually trying to do(?), you may be better off using a hash rather than an array.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: finding index number in an array
by Transient (Hermit) on May 27, 2005 at 22:16 UTC
    anything you do (besides List::Util first) will be "just as bad" as a foreach, because you'll have to loop through - even a grep is just a loop

    The bonus of "first" is that it is executed in a subroutine and can return immediately when it finds the first instance.

      The additional bonus of List::Util::first() is that it is compiled, so you will get performance akin to grep.

      I wrote a myfirst using for that shortcircuits like List::Util:

      Rate myfirst first myfirst 875/s -- -83% first 5236/s 498% --

      More interesting, possibly, is a comparison of grep and first solutions - does the slight overhead of calling first (a compiled, but still user, function) out-weight the benefit?

Re: finding index number in an array
by Anonymous Monk on May 27, 2005 at 22:44 UTC
    This is the simplest way I could find to do this. Do you think this will be fast enough for large N?
    my @list = qw(just remember in perl there's more than one way to do it +, not that you should do it any way.); my $i = 0; my %hash = map { $_ => ++$i } @list; my @search = ('to','just', 'should', 'one'); my @indices = grep { $hash{$_} } @search; print 'matches ' . join (", ", map { $_ => ord()-97 } map { (a...z)[$h +ash{$_}-1] } @indices);
Re: finding index number in an array
by Anonymous Monk on May 27, 2005 at 23:14 UTC
    Thank you all for your response.

Re: finding index number in an array
by Ninthwave (Chaplain) on May 28, 2005 at 00:41 UTC

    I never liked searches, I remember doing an address book application in the mid 80's and routines for finding names in a big list were a pain. But I found if you are looking for efficiency sort your array first if possible. Now this really only applies if your data is in a format that this can work with. For my names db then, I would sort my list by surname on entry so the list was always in alphabetical order. Than when give a name you I would go the half way point and if the $string ascii values were less than the half way point go back half way. And so on homing in on the name. I found this achieved quick results. But it relies on the dataset being easily sortable.

    This was in pascal and assembler and I have never really had to do anything since on the same scale because most datasets I work on now are coming from a Database Server which uses SQL to process the searches on that level. But I would think if the data is large enough and your searches happen often enough, you could build an index to use that you could than pull the values you need out of. Avoiding the overhead on the searches but adding the overhead on the data entry.

    "No matter where you go, there you are." BB
Re: finding index number in an array
by salva (Canon) on May 28, 2005 at 12:40 UTC
    sub grepix (&@) { my $test = shift; grep { local $_ = $_[$_]; &$test(); } (0..$#_) } my @index = grepix { /foo/ } qw(foofoo bar fo);