Skip to main content

PHP array comparison algorithm



While trying to simulate a bit of PHP behaviour I stumbled across this:







$a=array(0 => 1, 'test' => 2);

$b=array('test' => 3, 0 => 1);

var_dump($a==$b, $a>$b, $b>$a);







According to the output from var_dump $b is bigger than $a . In the PHP manual there is a Transcription of standard array comparison which states that the values of the arrays are compared one by one and if a key from the first array is missing in the second array, the arrays are uncomparable. So far so good. But if I try this (change in the first element of $a only):







$a=array(0 => 2, 'test' => 2);

$b=array('test' => 3, 0 => 1);

var_dump($a==$b, $a>$b, $b>$a);







All three comparison results are false . This looks like "uncomparable" to me (because the > result is the same as the < result, while the arrays are not == either, which makes no sense) but this does not fit the transcription from the PHP manual. Both keys are present in both arrays and I would expect $a to be bigger this time because the content of key 0 is bigger in $a (2 vs. 1).





I've tried to dig into the PHP source code and found zend_hash_compare() in zend_hash.c , but the code there seems to work as the manual describes.





What's going on here?


Comments

  1. It would seem that the comparison loop is in the case of > done over the right hand array and in the case of < done over the left hand array, ie always over the supposedly "lesser" array. The order of the elements is significant as the foreach loop in the transcription code respects array order.

    In other words;

    $a>$b loops over b and finds 'test' first. 'test' is greater in $b so $b is greater and it returns false.

    $b>$a loops over a and finds '0' first. '0' is greater in $a so $a is greater and it returns false.

    This would actually make sense, the "greater" array is then allowed to contain elements that the "lesser" array doesn't and still be greater as long as all common elements are greater.

    ReplyDelete
  2. EDIT: As Joachim has shown, it deals with the order called. To steal his words:
    "$a>$b loops over b and finds 'test' first. 'test' is greater in $b so $b is greater and it returns false. $b>$a loops over a and finds '0' first. '0' is greater in $a so $a is greater and it returns false."

    -- Original Post --

    I'm not 100% sure I'm right on this; I haven't seen this before, and have only briefly looked into it (major kudos, by the way, on an excellent question!). Anyway, it would appear that either PHP documentation is wrong, or this is a bug (in which case you might want to submit it), and here is why:

    in zend_hash_compare() in zend_hash.c, it seems as though there is some confusion over what ordered is (I'm looking at line 1514 and 1552-1561, which is my best guess is where the problem is, without doing lots of testing).

    Here's what I mean; try this:

    $a=array(0 => 2, 'test' => 2);
    $b=array(0 => 1, 'test' => 3);
    var_dump($a==$b, $a>$b, $b>$a);


    Note I merely switched the order of indexes, and $a>$b returns true. Also see this:

    $x=array(0 => 2, 'test' => 2);
    $y = $x;
    $y[0] = 1; $y['test'] = 3;
    var_dump($x==$y, $x>$y, $y>$x);


    Note here, as well, $x>$y returns true. In other words, PHP is not just matching array keys! It cares about the order of those keys in the arrays! You can prevent this situation by coming up with a "base" array and "copying" it into new variables (in my x/y example) before modifying, or you can create an object, if you so desire.

    To say all that differently, and much more briefly, it would appear that PHP is not just looking at key values, but at both key values AND key order.

    Again, I emphasize I don't know if this expected behavior (it seems like something they ought to have noted in the PHP manual if it was), or a bug/error/etc (which seems much more likely to me). But either way, I'm finding that it is compared first by number of keys (lines 1496-1501 in zend_hash.c), and then by both key value and key order.

    ReplyDelete
  3. I think here is comparing one by one so $a[0]>$b[0] but $a['test']<$b['test'].
    You can not say which array is bigger.

    ReplyDelete

Post a Comment

Popular posts from this blog

Why is this Javascript much *slower* than its jQuery equivalent?

I have a HTML list of about 500 items and a "filter" box above it. I started by using jQuery to filter the list when I typed a letter (timing code added later): $('#filter').keyup( function() { var jqStart = (new Date).getTime(); var search = $(this).val().toLowerCase(); var $list = $('ul.ablist > li'); $list.each( function() { if ( $(this).text().toLowerCase().indexOf(search) === -1 ) $(this).hide(); else $(this).show(); } ); console.log('Time: ' + ((new Date).getTime() - jqStart)); } ); However, there was a couple of seconds delay after typing each letter (particularly the first letter). So I thought it may be slightly quicker if I used plain Javascript (I read recently that jQuery's each function is particularly slow). Here's my JS equivalent: document.getElementById('filter').addEventListener( 'keyup', function () { var jsStart = (new Date).getTime()...

Is it possible to have IF statement in an Echo statement in PHP

Thanks in advance. I did look at the other questions/answers that were similar and didn't find exactly what I was looking for. I'm trying to do this, am I on the right path? echo " <div id='tabs-".$match."'> <textarea id='".$match."' name='".$match."'>". if ($COLUMN_NAME === $match) { echo $FIELD_WITH_COLUMN_NAME; } else { } ."</textarea> <script type='text/javascript'> CKEDITOR.replace( '".$match."' ); </script> </div>"; I am getting the following error message in the browser: Parse error: syntax error, unexpected T_IF Please let me know if this is the right way to go about nesting an IF statement inside an echo. Thank you.