array_diff_assoc() is considering 1 === "1" - php

I am running this code from php.net example
<?php
$array1 = array(0, 1, 2);
$array2 = array("00", "01", "2");
$result = array_diff_assoc($array1, $array2);
print_r($result);
?>
Output of which I am getting :
Array
(
[0] => 0
[1] => 1
)
php.net says : Two values from key => value pairs are considered equal only if (string) $elem1 === (string) $elem2 .
whereas in the example it considers 2 === "2".
How is this happening ? Please Explain ?
Might be the case
If $ele1 is casting integer into string i.e. 2 to "2", then why it is comparing with === operator. There might be 2 == "2" better option and we don't need to cast it into string. Please correct me, if I am wrong ?

If $ele1 is casting integer into string i.e. 2 to "2", then why it is comparing with === operator. There might be 2 == "2" better option and we don't need to cast it into string. Please correct me, if I am wrong ?It's just the way it is implemented.
If you want another comparision function use array_diff_uassoc()

Related

PHP Group tabels [duplicate]

If 0 could be a possible key returned from array_search(), what would be the best method for testing if an element exists in an array using this function (or any php function; if there's a better one, please share)?
I was trying to do this:
if(array_search($needle, $haystack)) //...
But that would of course return false if $needle is the first element in the array. I guess what I'm asking is is there some trick I can do in this if statement to get it to behave like I want it? I tried doing these, but none of them worked:
$arr = array(3,4,5,6,7);
if(array_search(3, $arr) != false) //...
if(array_search(3, $arr) >= 0) //...
Appreciate the help.
This is the way:
if(array_search(3, $arr) !== false)
Note the use of the === PHP operator, called identical (not identical in this case). You can read more info in the official doc. You need to use it because with the use of the equal operator you can't distinguish 0 from false (or null or '' as well).
A better solution if you find the match and want to use the integer value is:
if (($pos = array_search(3, $arr)) !== false)
{
// do stuff using $pos
}
As an alternative to === and !==, you can also use in_array:
if (in_array($needle, $haystack))
As an alternative to ===, !==, and in_array(), you can use array_flip():
$arr = [3,4,5,6,7];
$rra = array_flip($arr);
if( $rra[3] !== null ):
var_dump('Do something, '.$rra[3].' exists!');
endif;
This is not fewer lines than the accepted answer. But it does allow you to get position and existence in a very human-readable way.
We want to find out if int 3 is in $arr, the original array:
array (size=5)
0 => int 3
1 => int 4
2 => int 5
3 => int 6
4 => int 7
array_flip creates a new array ($rra) with swapped key-value pairs:
array (size=5)
3 => int 0
4 => int 1
5 => int 2
6 => int 3
7 => int 4
Now we can verify the existence of int 3, which will (or will not be) a key in $rra with array_key_exists, isset, or by enclosing $rra[3]` in an if statement.
Since the value we are looking for could have an array key id of 0, we need to compare against null and not rely on evaluating 0 to false.
You can also make changes to the original array, should that be the use case, because if $rra[3] isn't null, $rra[3] will return what is the key (position 0, in this case) for the value in the original array.
if ( $rra[3] !== null ):
// evaluates to: unset( $array[0] );
unset( $arr[ $rra[3] ] );
endif;
This operation works just as well if you're working with an associative array.

Determine if specific value exists in any level of a multidimensional array

For this nested array:
$status = array(
"house" => "OK",
"car" => array(
"car1" => "OK",
"car2" => "ERROR"
),
"boat" => "OK"
);
I want to know if a certain value "ERROR" exists at least once in the array. I don't care what key it's associated with, I just want to know if the $status array contains an "ERROR" status anywhere in it.
Is there a cleaner way to do this than just iterating over the elements with nested for loops?
You could use the function array_walk_recursive() to get all the values with any level of nested array.
https://secure.php.net/manual/en/function.array-walk-recursive.php
<?php
$status = array(
"house" => "OK",
"car" => array(
"car1" => "OK",
"car2" => "OK"
),
"boat" => "OK"
);
$required = array();
array_walk_recursive($status, function ($value, $key) use (&$required){
$required[] = $value;
}, $required);
print '<pre>';
print_r($required);
print '</pre>';
?>
Output:
Array
(
[0] => OK
[1] => OK
[2] => OK
[3] => OK
)
I actually wouldn't use array recursive. It falls short in a few key things you want to do. And while at first it may seem like a "shortcut" to what you want,if fails in a few key ways:
you can't return a simple testable value like a boolean true of false from the callback in it.
you can't build the $all array with nesting, because the keys that have an array as their value are never passed to the callback. This essentially flattens the array out.
it's not portable, or in other words it's not reusable as it's procedural in nature.
That said, honestly I wouldn't build the all array because it's pointless as you already have that array, so there is no point copying it. As I said I don't think you really want to rebuild $all but if you did you couldn't do it with array_walk_recursive even if you wanted to...
Anyway.
$array = ['zero','one', 'two' => ['zoo' => 'foo']];
function checkRecursive($value, $array){
$x = false;
foreach($array as $v){
if(is_array($v)) $x = checkRecursive($value, $v); //recursive
if($v == $value || $x == true) return true;
}
return false;
}
echo checkRecursive('one', $array) ? "true\n" : "false\n";
echo checkRecursive('foo', $array) ? "true\n" : "false\n";
echo checkRecursive('bar', $array) ? "true\n" : "false\n";
prints
true
true
false
Test it
http://sandbox.onlinephpfunctions.com/code/78d0de421475b8e3104376c698027107e9b7e948
UPDATE
I see you changed the question and while you say "value" your array says the "key"
$status = array(
"house" => "OK",
"car" => array(
"car1" => "OK",
"car2" => "OK"
),
"boat" => "OK"
);
Because all the values are the same. So if you really want to find the key instead then just change it to this
function checkRecursive($value, $array){
$x = false;
foreach($array as $k=>$v){
if(is_array($v)) $x = checkRecursive($value, $v); //recursive
if((string)$value == (string)$k || $x == true) return true;
}
return false;
}
A few things to note when checking for the keys, it's possible they will not always be strings, or that you will check using an unqoted number. In these cases PHP sometimes has a weird way of changing a string into an integer and non-numeric strings will come out as 0. For example "1" = 1 but "one" = 0 when taken as an integer. Therefore if you have an array with a key of 0 and you check for one it will match it because it will turn "one" into a 0.
You can solve this by casting everything to strings. Or by using the === strict type checking. I prefer the string method because checking with === would make 0 != "0" so if you tried finding a key of 0 with "0" it would say no match. This is true of other numbers. That is unless you plan on checking objects with it.
This is still an issue when looking for the "values" but it's less of one, because there you are not dealing with the native integer keys in an array, so there is a greater possibility you wouldn't encounter it.
It all has to do with PHP's loosely typed nature, it's a feature not a bug...
I put this small example togather:
echo '(int)"one": ';
echo (int)"one";
echo "\n";
echo "0 == 'one': " . (0 == "one" ? "true\n" : "false\n");
echo "1 == '1': " . (1 == "1" ? "true\n" : "false\n");
Outputs:
(int)"one": 0
0 == 'one': true
1 == '1': true
http://sandbox.onlinephpfunctions.com/code/df41f55251570a12d9ca693712bcdc785ad62f85

Why does array_uintersect_assoc() need the comparison function to return non-boolean values?

I wondered why array_uintersect_assoc()'s custom comparison function:
must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second
When I compare two arrays, I only need a boolean return value: the elements either match or they don't.
What's the actual reason of this behavior?
The function has been implemented this way to allow the usage of "classical" comparison functions which use such a return strategy. Such a function typically needs to be able to express three cases which is not possible with a boolean return value, for obvious reasons.
You can, however, also use a comparison function which does return a boolean result, since php as a weak typed language will automatically convert that for you. Take a look at that example which is a slightly modifed version of the one given in the function documentation:
<?php
function mystrcasecmp($a, $b) {
return strcasecmp($a, $b) ? true : false;
}
$array1 = array("a" => "green", "b" => "brown", "c" => "blue", "red");
$array2 = array("a" => "GREEN", "B" => "brown", "yellow", "red");
print_r(array_uintersect_assoc($array1, $array2, "mystrcasecmp"));
You can see that the comparison function used here returns a boolean, yet the result is exactly the same.
Bottom line: the existing implementation is more flexible, whilst allowing for the use of a comparison function returning a boolean result too.
I only need boolean value: the elements either match or they dont.
TL;DR
You need to invert the boolean value which will be converted to an int because these array_intersect and array_diff functions that call custom functions only qualify data that returns a zero-ish result (i.e.: null, false, "0", 0, empty string, empty array). Here is a ternary implementation:
array_uintersect_assoc($array1, $array2, fn($a, $b) => str_contains($a, $b) ? 0 : 1)
The explanation...
It's easy to get confused with this operation. Your question led me to post Unexpected results with array_uintersect_assoc() when callback returns non-numeric string.
Let's say you want to use array_uintersect_assoc() and you have these two input arrays:
$array1 = ["a" => "green", "b" => "brown", "c" => "blue", 0 => "red"];
$array2 = ["a" => "GREEN", "B" => "brown", 0 => "yellow", 1 => "red", "c" => "blue"];
Now let's say you want to make a custom function call which returns a boolean value. I'll nominate PHP8's str_contains() which will be sufficient for this demonstration. The first array will contain the haystack strings and the second array will contain the needle strings.
var_export(array_uintersect_assoc($array1, $array2, 'str_contains'));
This will check for identical keys from the first array in the second array THEN among those qualifying elements, it will check if the second array's value is found within the first array's string. Being an "intersect" call, you would intuitively expect:
['c' => 'blue']
because only the c-keyed element in the second array has a value where the second array's value case-sensitively exists in the first array's value.
However, what you actually get is:
['a' => 'green', 0 => 'red']
What?!? The reason that you get elements with keys a and 0 in the result is because any of the array_ diff/intersect functions that include u in their name are making a qualifying match when a 0 result is returned.
When the c elements' values are fed to str_contain(), a true boolean is returned. array_uintersect_assoc() then forces the boolean value to be converted to an int type. When converting booleans to ints, false becomes 0 and true becomes 1.
To fix this behavior to get the intended result, you cannot simply change the intersect word inside of the function's name to diff -- that creates:
['b' => 'brown', 'c' => 'blue']
This is because b doesn't have an identical corresponding key in the second array. c does have an identical corresponding key, and the true result from str_contains() is evaluated as "don't keep" by array_udiff_assoc().
Finally, the fix is to invert the boolean value so that a true becomes 0 and a false becomes a non-zero. (Demo)
var_export(
array_udiff_assoc(
$array1,
$array2,
fn($a, $b) => str_contains($a, $b) ? 0 : 1;
// or !str_contains($a, $b) until the day when PHP throws a DEPRECATED warning for returning a boolean
)
);

What is the reverse of (object) array (key => value) in PHP?

Is there a fast way to return a key when you know its value in PHP?
For example, if you have:
$Numbers = (object) array ( "0" => 0, "1" => 1, "2" => 3, "3" => 7, "4" => 13 );
is there a way to return:
echo re(13); // Print 4
One way I could think of is to build a function specifically for this, but I was wondering if there is a better way.
There is array_search:
$key = array_search(13, (array) $Numbers);
See http://ua2.php.net/array_search
As several other people have mentioned, array_search does what you asked. But if you want an array of ALL the keys that contain the value instead of only the first, you can use array_keys.
print_r(array_keys($Numbers, 13)); // print Array ( [0] => 4 )
http://www.php.net/manual/en/function.array-keys.php
if you are certain that you have unique keys:
$flipped = array_flip($array);
$key = $flipped[$knownValue];
return $key;
Otherwise, use array_search:
return array_search($knownValue, $array);
The first approach may be better if you do a number of lookups, and have unique values. The second approach returns the first matching key in case of multiple values.
http://php.net/array_search
echo array_search($valToSearch, (array) $Numbers);
http://php.net/manual/en/function.array-search.php

Checking if value exists in array with array_search()

If 0 could be a possible key returned from array_search(), what would be the best method for testing if an element exists in an array using this function (or any php function; if there's a better one, please share)?
I was trying to do this:
if(array_search($needle, $haystack)) //...
But that would of course return false if $needle is the first element in the array. I guess what I'm asking is is there some trick I can do in this if statement to get it to behave like I want it? I tried doing these, but none of them worked:
$arr = array(3,4,5,6,7);
if(array_search(3, $arr) != false) //...
if(array_search(3, $arr) >= 0) //...
Appreciate the help.
This is the way:
if(array_search(3, $arr) !== false)
Note the use of the === PHP operator, called identical (not identical in this case). You can read more info in the official doc. You need to use it because with the use of the equal operator you can't distinguish 0 from false (or null or '' as well).
A better solution if you find the match and want to use the integer value is:
if (($pos = array_search(3, $arr)) !== false)
{
// do stuff using $pos
}
As an alternative to === and !==, you can also use in_array:
if (in_array($needle, $haystack))
As an alternative to ===, !==, and in_array(), you can use array_flip():
$arr = [3,4,5,6,7];
$rra = array_flip($arr);
if( $rra[3] !== null ):
var_dump('Do something, '.$rra[3].' exists!');
endif;
This is not fewer lines than the accepted answer. But it does allow you to get position and existence in a very human-readable way.
We want to find out if int 3 is in $arr, the original array:
array (size=5)
0 => int 3
1 => int 4
2 => int 5
3 => int 6
4 => int 7
array_flip creates a new array ($rra) with swapped key-value pairs:
array (size=5)
3 => int 0
4 => int 1
5 => int 2
6 => int 3
7 => int 4
Now we can verify the existence of int 3, which will (or will not be) a key in $rra with array_key_exists, isset, or by enclosing $rra[3]` in an if statement.
Since the value we are looking for could have an array key id of 0, we need to compare against null and not rely on evaluating 0 to false.
You can also make changes to the original array, should that be the use case, because if $rra[3] isn't null, $rra[3] will return what is the key (position 0, in this case) for the value in the original array.
if ( $rra[3] !== null ):
// evaluates to: unset( $array[0] );
unset( $arr[ $rra[3] ] );
endif;
This operation works just as well if you're working with an associative array.

Categories