http://www.perlmonks.org?node_id=681629

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

Hello monks, I would like to know if there is a quick way of testing if an element exists in an array. I know that the apprpriate way to go is with hashes, but I cannot have unique keys so I cannot construct a hash.
My file has something like:
Jim 12 John 15 Peter 08 Andrew 34 Jim 57 Andreas 27
so I cannot build a hash... Note that I MUST search the file using the names, there is no other option... Of course If I can do something different that build an array with the names and search it.

Replies are listed 'Best First'.
Re: check if an element exists in array
by GrandFather (Saint) on Apr 19, 2008 at 08:56 UTC

    For focused advice you need to give us the bigger picture. If the search is occasional or the array is small (supply your own definition for both those) then grep is the tool of choice. If you don't care about the original order of the data and the data set is of modest size (again supply your own definition) then a hash of arrays may be appropriate. If the data size is modest (ditto) but you need order information add an ordinal along with the data value for each item (see below). If the data is large, use a database.

    For the penultimate option consider:

    use strict; use warnings; my $entries = 0; my %data; while (<DATA>) { chomp; my ($name, $value) = split ' ', $_, 2; push @{$data{$name}}, [$entries++, $value]; } for my $name (sort keys %data) { print "$name\n"; print " $_->[1]\n" for sort {$a->[0] <=> $b->[0]} @{$data{$name} +}; } __DATA__ Jim 12 John 15 Peter 08 Andrew 34 Jim 57 Andreas 27

    Prints:

    Andreas 27 Andrew 34 Jim 12 57 John 15 Peter 08

    Perl is environmentally friendly - it saves trees
Re: check if an element exists in array
by FunkyMonk (Chancellor) on Apr 19, 2008 at 08:38 UTC
    If all you want to do is check if the name is in the list then a hash is the way to go. But, to search an AoA, you could use grep:
    my @names = ( [ qw/Jim 12/ ], [ qw/John 15/ ], [ qw/Peter 08/ ], [ qw/Andrew 34/ ], [ qw/Jim 57/ ], [ qw/Andreas 27/ ], ); my $name = 'Jim'; #simple searching if ( grep { $_->[0] eq $name } @names ) { print "$name\'s in the list\n"; } # or to get all of $name's entries for ( grep { $_->[0] eq $name } @names ) { print "@$_\n"; }

    Output:

    Jim's in the list Jim 12 Jim 57


    Unless I state otherwise, my code all runs with strict and warnings
      I wanted to use a hash, but how will I build it? I do not have unique names...
        Using @names as before...
        my %names_h = map { $_->[0] => 1 } @names; # map { @$_ } @names; # similar, but more scary my $name = 'Jim'; print "Jim's in the hash\n" if $names_h{$name};


        Unless I state otherwise, my code all runs with strict and warnings
Re: check if an element exists in array
by moritz (Cardinal) on Apr 19, 2008 at 08:36 UTC
    You can build a hash, but perhaps you need to store arrays as the hash values, not the strings directly.

    Or you can build an array and a hash, and preserve multiple names only in the array.

    Or of course you can just iterate over the array to find the names, but that's very inefficient, especially if you do it repeatedly.

Re: check if an element exists in array
by Not_a_Number (Prior) on Apr 19, 2008 at 11:55 UTC

    As you can no doubt deduce from the replies that you have received so far, your requirements remain vague. The question implied by your title, "check if an element exists in array", has been more than adequately dealt with. But from your later comments, this is not what you really want.

    Please correct me if I'm wrong, but it seems to me that your real requirements are as follows:

    • You have a file, in the format indicated in your OP.
    • You get input from somewhere, consisting of a name.
    • If the name exists in the first column of the file, you want to output it, followed by the corresponding number in the second column.

    What is not clear, however, is what output you want if the name occurs more than once in the file, ie for input of 'Jim' in your example, I see several possibilities:

    Jim 12 # Use first occurrence Jim 57 # Use last occurrence Jim 12, 57 # List of all numbers associated with Jim Jim 69 # Jim's total 'score' Sorry, name 'Jim' occurs more than once # Or some similar warning

    Based on the answers provided above, you should be able to code for any of these. What I suggest is that you now write some code, and come back to us if you still can't get it to do what you want. But please, in that case, tell us what it is that you want!

Re: check if an element exists in array
by mscharrer (Hermit) on Apr 19, 2008 at 09:23 UTC
    Is the order of the lines meaningfull?
    If not you could use a hash which holds an array with all the values:

    my %people; while( my $line = <IN> ) { my ($name, $value) = split /\s+/, $line; if (!exists $people{$name}) { $people{$name} = [ $value ]; # store array with one value } else { push @{$people{$name}}, $value; # push 2nd,3rd,.. value on arr +ay } }
    The %people hash would then look like:
    %people = ( 'Andreas' => [ '27' ], 'John' => [ '15' ], 'Andrew' => [ '34' ], 'Jim' => [ '12', '57' ], 'Peter' => [ '08' ] );
    This preserves the order of the values but not the order of the names.
      You can save yourself some typing as Perl will auto-vivify the anonymous array for you if it doesn't exist already. Your

      if (!exists $people{$name}) { $people{$name} = [ $value ]; # store array with one value } else { push @{$people{$name}}, $value; # push 2nd,3rd,.. value on arr +ay }

      can become

      push @{$people{$name}}, $value;

      I hope this is of interest.

      Cheers,

      JohnGG

Re: check if an element exists in array
by jethro (Monsignor) on Apr 19, 2008 at 11:06 UTC
    If the Hash of Arrays is too high tech for you, you could also simply concatenate the numbers in a comma seperated string, so that Jims entry would be "12,57".

    The if statement in mscharrers code would then look like:

    if (!exists $people{$name}) { $people{$name} = $value; # store array with one value } else { $people{$name} .= ",$value"; # push 2nd,3rd,.. value on array
    You can just print the string stored in the hash or get at the numbers with my @numbers= split /,/,$value
Re: check if an element exists in array
by elmex (Friar) on Apr 19, 2008 at 09:08 UTC

    If the task at hand is just to check whether the name is in the file the best would probably to analyze the contents of the file directly:

    my $name = 'Jim'; my $name_len = length $name; while (<STDIN>) { if (substr ($_, 0, $name_len) eq $name) { print "Jim exists!\n"; last; } }

    Of course I doubt that this is the only thing you want to do with that data, but the less processing of the data you do, and the easier the solution, the faster it usually is :) But note that my code is just a concept code. If someone named 'Jimbo' would be in that file it would also match. The correct solution depends on which delimiter you use to seperate the name form the numbers.

    As said in above comments: There is the rule that for low n O(n) is equal to O(1) :-) So a grep is usually fine for n =~ 100.

Re: check if an element exists in array
by Tux (Canon) on Apr 14, 2011 at 06:21 UTC
    use List::Util qw(first); first { $_ eq $needle } @list and say "Found!";

    Enjoy, Have FUN! H.Merijn
Re: check if an element exists in array
by RecursionBane (Beadle) on Apr 12, 2011 at 01:43 UTC
    For those getting here through the Google terms "element exists in array perl", here's a simple routine I wrote because I wanted something similar to TCL's "lsearch -exact" but with a binary return:
    # Checks if a provided element exists in the provided list # Usage: isInList <needle element> <haystack list> # Returns: 0/1 sub isInList { my $needle = shift; my @haystack = @_; foreach $hay (@haystack) { if ( $needle eq $hay ) { return 1; } } return 0; }
    Of course, this should only be used on small haystacks (a few hundred elements at most).
      sub isInList { my( $d, $it ) = ( "\0", @_ ); join( $d, '', @_, '' ) =~ /$d\Q$it\E$d/ }

      Caveat: This treats all the values as strings. Input values which are not strings will get stringified! This may or may not yield the desired results.

        Elegant! Does Perl automagically return the last modified variable when returning from a subroutine?