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?

2

5 Answers 5

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

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

6 Comments

++. 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};
undef @hash{@array} works but isn't actually documented to do so. use @hash{@array} = () instead.
I wasn't sure what you meant, so I googled and found this: perlmonks.org/?node_id=435223 Thanks for the correction, ysth!
grep is bad - OP explicitly said "without looping through every element".
@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.
|
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

3 Comments

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.
Also if efficiency is the main concern, just do a manual foreach with last.
@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.
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.

1 Comment

It seems like any is a better choice. What if I'm looking for a falsey value?
0
if ( grep /^80$/, @int_array ) {
    ...
}

9 Comments

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.
@gab: in typical cases, it's cheaper to keep going through the array than to bail out early.
@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?
@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.
@ysth - can you elaborate why you consider full array scan cheaper than bailing out early?
|
0

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

my $found = (80 ~~ $in_array);

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.