Which is faster: in_array() or a bunch of expressions in PHP?
Asked Answered
G

10

19

Is it faster to do the following:

 if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') { ... }

Or:

 if (!in_array($var, array('test1', 'test2', 'test3', 'test4') { ... }

Is there a number of values at which point it's faster to do one or the other?

(In this case, the array used in the second option doesn't alreay exist.)

Gamine answered 27/11, 2008 at 21:17 Comment(1)
Off topic slightly - did you just delete your other question from a few minutes ago, or am I going crazy?Jat
D
21

i'd strongly suggest just using in_array(), any speed difference would be negligible, but the readability of testing each variable separately is horrible.

just for fun here's a test i ran:

$array = array('test1', 'test2', 'test3', 'test4');
$var = 'test';
$iterations = 1000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') {}
}
$end = microtime(true);

print "Time1: ". ($end - $start)."<br />";

$start2 = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!in_array($var, $array) ) {}
}
$end2 = microtime(true);

print "Time2: ".($end2 - $start2)."<br />";

// Time1: 1.12536692619
// Time2: 1.57462596893

slightly trivial note to watch for, if $var is not set, method 1 takes much longer (depending on how many conditions you test)

Update for newer PHP versions:

Martijn: I'ved extended the array to five elements, and look for test3, as sort of an average case.

PHP5.6

Time1: 0.20484399795532
Time2: 0.29854393005371

PHP7.1

Time1: 0.064045906066895
Time2: 0.056781053543091

PHP7.4

Time1: 0.048759937286377
Time2: 0.049691915512085

PHP8.0

Time1: 0.045055150985718
Time2: 0.049431085586548

Conclusion: The original test wasnt the best test, and also: In php7+ is has become a matter of preference.

Demilitarize answered 27/11, 2008 at 21:21 Comment(10)
What version of PHP are you on? 5.2.6 (windows) gives Time1: 1.36 Time2: 4.88 - that IS a big differenceJat
Also you're cheating slightly by taking the array(...) out of the loop - if you use it as the question says, my time2 goes to over 6 secondsJat
5.2.5 (mac os x), i'm getting ~1.25 : ~4 with the non-cheating versionDemilitarize
Can we assume that the question author would not stipulate the array be declared inline if he were running the code inside a tight loop?Disciple
How is moving the array() initialization "cheating"? The loop is irrelevant to actual code. And if one assumes that the actual code in question may be executed many, many times, then of course, the OP would set a variable equal to the array of items he was searching first.Viscid
I think the worst part is that your tests don't correct him and uses the array backwards :(! If you have the freedom to search $string == 'value1', then you also should have the freedom to flip the array beforehand and use $array = array('test1'=>1, 'test2'=>1, 'test3'=>1, 'test4'=>1);, which will be DRMATICALLY. This test is like using the handle of the hammer to drive a nail :/. I mean, why use an O(n) lookup for the array when you have access to an O(1) lookup.Penmanship
Just as a fun data point, I ran those on PHP 7.1.8. Time1: 0.049320936203003, Time2: 0.025889873504639.Zabaglione
My times on PHP 7.1 -- Time1: 0.11170816421509, Time2: 0.06308913230896Giddings
So we stick to in_array() rather than the usual comparison... Correct ?Moores
@Demilitarize and what about in_array VS match?Cholula
R
10

Note that if you're looking to replace a bunch of !== statements, you should pass the third parameter to in_array as true, which enforces type checking on the items in the array.

Ordinary != doesn't require this, obviously.

Ridglee answered 28/11, 2008 at 1:43 Comment(0)
J
6

The first will be faster - the second has a lot of overhead: creating the array, calling a function, searching the array...

However, as I said in a question a couple of answers down, premature optimization is the root of all evil. You should write your code to be readable, then if it needs to be optimized profile it, then optimize.

Edit:

My timings with @Owen's code (PHP 5.2.6 / windows):

Time1: 1.33601498604
Time2: 4.9349629879

Moving the array(...) inside the loop, as in the question:

Time1: 1.34736609459
Time2: 6.29464697838
Jat answered 27/11, 2008 at 21:22 Comment(2)
Oops, sorry for typo-ing your name Owen!Jat
As mentioned in the comments of the other answer, it appears this has been optimized in PHP7+, as both methods would take just 25 - 50 milliseconds, instead of 1 - 6 seconds.Fluoroscopy
S
6

Here is an live update of this bench with another case https://3v4l.org/OA2S7

The results for PHP 7.3:

  • multiple comparisons: 0.057507991790771

  • in_array: 0.02568507194519

  • array_flip() outside loop measured + isset(): 0.014678001403809

  • array_flip() outside loop not measured + isset(): 0.015650033950806

  • foreach and comparison: 0.062782049179077

Shrieval answered 18/3, 2019 at 18:19 Comment(0)
R
4

Hi I just took this case to extremes and pointed out that with increasing number of values plain comparison is not the most performant way.

Here is my code:

$var = 'test';
$num_values = 1000;
$iterations = 1000000;
print "\nComparison performance test with ".$num_values." values and ".$iterations." loop iterations";
print "\n";

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if ($var != 'test0' &&
        $var != 'test1' &&
        // ...
        // yes I really have 1000 lines in my file
        // ...
        $var != 'test999') {}
}
print "\nCase 1: plain comparison";
print "\nTime 1: ". (microtime(true) - $start);
print "\n";

$start = microtime(true);
$array = array();
for($i=0; $i<$num_values; $i++) {
    $array1[] = 'test'.$i;
}
for($i = 0; $i < $iterations; ++$i) {
    if (!in_array($var, $array1) ) {}
}
print "\nCase 2: in_array comparison";
print "\nTime 2: ".(microtime(true) - $start);
print "\n";

$start = microtime(true);
$array = array();
for($i=0; $i<$num_values; $i++) {
    $array2['test'.$i] = 1;
}
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array2[$var])) {}
}
print "\nCase 3: values as keys, isset comparison";
print "\nTime 3: ".(microtime(true) - $start);
print "\n";

$start = microtime(true);
$array = array();
for($i=0; $i<$num_values; $i++) {
    $array3['test'.$i] = 1;
}
for($i = 0; $i < $iterations; ++$i) {
    if (!array_key_exists($var, $array3)) {}
}
print "\nCase 4: values as keys, array_key_exists comparison";
print "\nTime 4: ".(microtime(true) - $start);
print "\n";

My Results (PHP 5.5.9):

Case 1: plain comparison
Time 1: 31.616894006729

Case 2: in_array comparison
Time 2: 23.226133823395

Case 3: values as keys, isset comparison
Time 3: 0.050863981246948

Case 4: values as keys, array_key_exists comparison
Time 4: 0.13700890541077

I agree, thats a little extreme but it shows the big picture and the great potential in the hash-table-like associative arrays of PHP, you just have to use it

Rilda answered 11/12, 2014 at 8:52 Comment(0)
D
2

in_array will be faster for large numbers of items. "large" being very subjective based on a lot of factors related to the data and your computer. Since you are asking, I assume you are not dealing with a trivial number of items. For longer lists, heed this information, and measure performance with a flipped array so that php can utilize hash lookups instead of a linear search. For a "static" array that tweak may not improve performance, but it also may.

Using Owen's test code, with a flipped array and more iterations for more consistent results:

$array2 = array_flip($array);
$iterations = 10000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array2[$var])) {}
}
$end = microtime(true);
print "Time3: ".($end - $start)."<br />";

Time1: 12.875
Time2: 13.7037701607
Time3: 3.70514011383
Disciple answered 27/11, 2008 at 21:26 Comment(2)
Small comment: flipping a large array could use a lot of memory.Gamine
The array can be declared in the flipped configurationDisciple
L
1

Note that as RoBorg pointed out, there's overhead in creating the array so it should be moved inside the iteration loop. For this reason, Sparr's post is also a little misleading as there's overhead with the array_flip function.

Here's another example with all 5 variations:

$array = array('test1', 'test2', 'test3', 'test4');
$var = 'test';
$iterations = 1000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
   if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') {}
}
print "Time1: ". (microtime(true) - $start);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
   if (!in_array($var, $array) ) {}
}
print "Time2: ".(microtime(true) - $start);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
   if (!in_array($var, array('test1', 'test2', 'test3', 'test4')) ) {}
}
print "Time2a: ".(microtime(true) - $start);

$array2 = array_flip($array);
$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
  if (!isset($array2[$var])) {}
}
print "Time3: ".(microtime(true) - $start);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    $array2 = array_flip($array);
  if (!isset($array2[$var])) {}
}
print "Time3a: ".(microtime(true) - $start);

My results:

Time1 : 0.59490108493 // straight comparison
Time2 : 0.83790588378 // array() outside loop - not accurate
Time2a: 2.16737604141 // array() inside loop
Time3 : 0.16908097267 // array_flip outside loop - not accurate
Time3a: 1.57209014893 // array_flip inside loop

In summary, using array_flip (with isset) is faster than inarray but not as fast as a straight comparison.

Lexy answered 16/2, 2009 at 4:4 Comment(2)
Don't do the array flip in the loop. Change it to $array2 = array('test1'=>0,'test2'=>0,'test3'=>0,'test4'=>0); and try again. In the first case the array is constructed so in this case you should also construct the array.Fanfaron
on the above comment, isset will return false on 0 as the val, better to use 1 as the val or use array_key_existsScreamer
I
1

When speaking of PHP, and asking whether:

  • a set of "if"s and "else ifs" ,
  • an "if" with a set of "or"ed conditions (as in the original post details) , or
  • use of "in_array" with an on-the-fly constructed array ,

is better,

one should keep in mind that the PHP language "switch" statement is an alternative designed for such situations and may be a better answer. (Although the poster's example leads us to just comparing two solutions, the actual question heading asks to consider in_array versus PHP statements, so I think this is fair game).

In the poster's example, then, I would instead recommend:

switch ($var)
{ case 'test1': case 'test2': case 'test3': case 'test4':
     echo "We have a good value"; break;
  default:
     echo "We do not have a good value";
}

I wish PHP allowed for a couple of non-primitive constructs in the cases, such as a comma for "or". But the above is what the designers of PHP considered to be the clearest way of handling this. And it appears to be more efficient at execution time than the other two alternatives, as well.

As long as I'm talking about a wishlist, the "IN" found in SQL would be even clearer for the poster's example situation.

This thinking is probably what leads to people wanting to use "in_array", for such situations, but it is kind of unfortunate to have to build a data structure and then use a predicate designed for that data structure, rather than having a way to just say it without that overhead happening.

Intumesce answered 21/6, 2015 at 20:12 Comment(0)
I
0

I know this question is nearly 10 years old, but there are other ways to do this. I used method B from Nick's page with thousands of entries. It was incredibly fast.

foreach(array_values($haystack) as $v)
    $new_haystack[$v] = 1; 
}

// So haystack becomes:
$arr[“String1”] = 1;
$arr[“String2”] = 1;
$arr[“String3”] = 1;


// Then check for the key:
if (isset($haystack[$needle])) {
    echo("needle ".$needle." found in haystack");
}
Idel answered 27/8, 2017 at 7:52 Comment(0)
C
0

My Testing

$array = array('test1', 'test2', 'test3', 'test4');
$var = 'test';
$iterations = 1000000;

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if ($var != 'test1' && $var != 'test2' && $var != 'test3' && $var != 'test4') {}
}
$end = microtime(true);

print "Time1: ". ($end - $start)."<br />";

$start2 = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!in_array($var, $array) ) {}
}
$end2 = microtime(true);

print "Time2: ".($end2 - $start2)."<br />";

$array_flip = array_flip($array);

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array_flip[$var])) {}
}
$end = microtime(true);
print "Time3: ".($end - $start)."<br />";

$start = microtime(true);
for($i = 0; $i < $iterations; ++$i) {
    if (!isset($array[$var])) {}
}
$end = microtime(true);

print "Time4: ". ($end - $start)."<br />";

Time1: 0.20001101493835

Time2: 0.32601881027222

Time3: 0.072004079818726

Time4: 0.070003986358643

Concerted answered 8/2, 2019 at 23:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.