19

If I have an array:

@int_array = (7,101,80,22,42);

How can I check if the integer value 80 is in the array without looping through every element?

coding4fun
  • 203
  • 1
  • 2
  • 5
  • 1
    Looks like a duplicate: http://stackoverflow.com/questions/2860226 – Calimo Jun 10 '13 at 10:05
  • Possible duplicate of [How can I check if a Perl array contains a particular value?](http://stackoverflow.com/questions/2860226/how-can-i-check-if-a-perl-array-contains-a-particular-value) – ThisSuitIsBlackNot Nov 02 '15 at 10:14

5 Answers5

35

You can't without looping. That's part of what it means to be an array. You can use an implicit loop using grep or smartmatch, but there's still a loop. If you want to avoid the loop, use a hash instead (or in addition).

# grep
if ( grep $_ == 80, @int_array ) ...

# smartmatch
use 5.010001;
if ( 80 ~~ @int_array ) ...

Before using smartmatch, note:

http://search.cpan.org/dist/perl-5.18.0/pod/perldelta.pod#The_smartmatch_family_of_features_are_now_experimental:

The smartmatch family of features are now experimental

Smart match, added in v5.10.0 and significantly revised in v5.10.1, has been a regular point of complaint. Although there are a number of ways in which it is useful, it has also proven problematic and confusing for both users and implementors of Perl. There have been a number of proposals on how to best address the problem. It is clear that smartmatch is almost certainly either going to change or go away in the future. Relying on its current behavior is not recommended.

Warnings will now be issued when the parser sees ~~, given, or when. To disable these warnings, you can add this line to the appropriate scope

Community
  • 1
  • 1
ysth
  • 96,171
  • 6
  • 121
  • 214
  • ++. Smart match (or maybe something from List::MoreUtils) is the way to go. For completeness, here's how you could do it with a hash: my @array = 1..1000; my %hash; undef @hash{@array}; say "10 is in the array!" if exists $hash{10}; – Hugmeir Dec 31 '10 at 14:56
  • 2
    `undef @hash{@array}` works but isn't actually documented to do so. use `@hash{@array} = ()` instead. – ysth Dec 31 '10 at 15:00
  • I wasn't sure what you meant, so I googled and found this: http://www.perlmonks.org/?node_id=435223 Thanks for the correction, ysth! – Hugmeir Dec 31 '10 at 15:05
  • grep is bad - OP explicitly said "without looping through every element". – DVK Dec 31 '10 at 15:09
  • @ysth - Original poster asked how to find the element **without looping**. `grep` loops through the whole list, so it can't be a solution to OP's question (though smart match in Perl5.10+ of course is a great solution) – DVK Dec 31 '10 at 16:26
  • 2
    @DVK: that's why I say "You can't". smartmatch or anything else is just as much a loop (potentially going through every element) as grep or for. – ysth Dec 31 '10 at 16:41
9

CPAN solution: use List::MoreUtils

use List::MoreUtils qw{any}; 
print "found!\n" if any { $_ == 7 } (7,101,80,22,42);

If you need to do MANY MANY lookups in the same array, a more efficient way is to store the array in a hash once and look up in the hash:

@int_array{@int_array} = 1;
foreach my $lookup_value (@lookup_values) {
    print "found $lookup_value\n" if exists $int_array{$lookup_value}
}

Why use this solution over the alternatives?

  • Can't use smart match in Perl before 5.10. According to this SO post by brian d foy]2, smart match is short circuiting, so it's as good as "any" solution for 5.10.

  • grep solution loops through the entire list even if the first element of 1,000,000 long list matches. any will short-circuit and quit the moment the first match is found, thus it is more efficient. Original poster explicitly said "without looping through every element"

  • If you need to do LOTs of lookups, the one-time sunk cost of hash creation makes the hash lookup method a LOT more efficient than any other. See this SO post for details

Community
  • 1
  • 1
DVK
  • 126,886
  • 32
  • 213
  • 327
  • Well it depends on the size of the array and the number of checks you need to make. Parsing an external module might be less efficient than a full `grep`. – Matteo Riva Dec 31 '10 at 15:15
  • Also if efficiency is the main concern, just do a manual `foreach` with `last`. – Matteo Riva Dec 31 '10 at 15:16
  • 2
    @kemp - IIRC, List::MoreUtils has an XS implementation. That is faster than foreach with last. if using native Perl, that's probably what the module does anyway. – DVK Dec 31 '10 at 15:17
3

Yet another way to check for a number in an array:

#!/usr/bin/env perl

use strict;
use warnings;

use List::Util 'first';

my @int_array       = qw( 7 101 80 22 42 );
my $number_to_check = 80;

if ( first { $_ == $number_to_check } @int_array ) {
    print "$number_to_check exists in ", join ', ', @int_array;
}

See List::Util.

Alan Haggai Alavi
  • 72,802
  • 19
  • 102
  • 127
0
if ( grep /^80$/, @int_array ) {
    ...
}
Matteo Riva
  • 24,728
  • 12
  • 72
  • 104
  • Using grep in general for this is not such a good idea; it'll allocate an extra list to hold the results of the grep, and it'll keep going through the array even if the first element is 80. – Gabriel Reid Dec 31 '10 at 14:50
  • @gab: in typical cases, it's cheaper to keep going through the array than to bail out early. – ysth Dec 31 '10 at 14:54
  • 1
    @gab: in general I avoid coding Perl like C -- if Perl offers convenient tools and I don't use them, why using Perl at all? – Matteo Riva Dec 31 '10 at 14:56
  • @kemp: I agree completely -- however, the Perl way of doing quick checks to see if a value is present is to use a hash instead of a list. – Gabriel Reid Dec 31 '10 at 15:03
  • Well hashes are not substitutes of lists, or we wouldn't be using lists at all – Matteo Riva Dec 31 '10 at 15:08
  • OP explicitly said "without looping through every element", so you can't use `grep` – DVK Dec 31 '10 at 15:09
  • 1
    @ysth - can you elaborate why you consider full array scan cheaper than bailing out early? – DVK Dec 31 '10 at 15:10
  • @DVK: Bailing out early involves more complicated code that takes longer to run per iteration. And, judging by the questions people ask, usually the arrays are not long enough to make it worth it (and as always, more code means more possibility of bugs). Using a hash is best; if for some reason that doesn't make sense, for 5.10.1+ use smartmatch. Otherwise, use grep, unless you actually notice performance problems. Feel free to play with http://perl.pastebin.com/kEs5VktK and see what you conclude. – ysth Dec 31 '10 at 16:16
  • @ysth - I guess I'm biased - I more often than not have to deal with huge data sets (and always do the hash lookups for small ones). I agree that on 3-7 element lists you are probably right about extra branching costing more. But my assumption is that the questioneer merely uses small array as an example, and is concerned about big-O performance trends and not exact benchmarking with very small list. – DVK Dec 31 '10 at 16:29
0

If you are using Perl 5.10 or later, you can use the smart match operator ~~:

my $found = (80 ~~ $in_array);
aschepler
  • 70,891
  • 9
  • 107
  • 161