I would like to know which is the fastest way to check that two (possibly multidimensional) arrays contain the same values.
In other terms I would like to know if the (unordered) set of values of the first array is equal to the set of values of the second array
UPDATE:
1) cannot use == or ===. They check also for key equality.
2) cannot use array_diff. It doesn't work with multidimensional arrays
There's no simple way to do this for a nested array; you'll have to write your own recursive function. It looks like array_udiff isn't really suitable, either (due to requiring greater-than/less-than information from the comparison function);
This should do the trick:
function recursive_sort_array($a) {
if(!is_array($a))
return $a;
$a = array_map('recursive_sort_array', $a);
sort($a);
return array_values($a);
}
function arrays_different($a, $b) {
return recursive_sort_array($a) === recursive_sort_array($b);
}
What it does: The first function (recursive_sort_array) recursively sorts an array and returns only the values, tied to numeric indexes. Any arrays that have the same values (recursively) will be equal after this operation, and can then be compared with one of the equality operators.
Related
I have two multidimensional arrays, each element in the array consists of 2 elements, the first is a string and the second is an integer. I want to get the difference between the two multidimensional arrays based on the second value if and only if the first elements are equal. I am using array_udiff such as below:
$arrdiff = array_udiff($arr1, $arr2, 'udiffCompare');
I implemented the function array_udiff such that if the first element is different to return them as equal since I don't want it to appear in the difference, and if the first element is equal then compare the second element and return accordingly, below is the function I implemented
function udiffCompare($a, $b) {
return strcmp($a[0], $b[0]) == 0 ? $ a[1] - $b[1] : 0;
}
However, even though I have two arrays with the same first element but a different second element, they are not returned in the result of array_udiff function.
Am I missing anything here? Any help is appreciated.
The problem is, you're looking for a difference within an intersection, using only a difference function.
Try computing the intersection based on the string value, and using the result to compute the difference based on the int value.
function sameString ($a, $b) {
return strcmp($a[0], $b[0]);
}
function differentInt($a, $b) {
return $a[1] - $b[1];
}
$diff = array_udiff(array_uintersect($arr1, $arr2, 'sameString'), $arr2, 'differentInt');
I have to arrays and want to check if they have the same values, not matters the order, so I used == operator:
$roles = ['admin', 'manager'];
$needle = ['manager', 'admin'];
$needle == $roles; // false
The problem is that with same values but in different order, the operator evaluates the comparison to false.
How to properly compare two arrays to check if values are the same?
UPDATE
For now, I'm going with array_intersect:
$hasExactRoles = (
(array_intersect($roles, $needle) === $roles) &&
(array_intersect($needle, $roles) === $needle)
);
You can check what is a difference between arrays.
$roles = ['admin', 'manager'];
$needle = ['manager', 'admin'];
if (empty(array_diff($roles, $needle))) {
echo 'The same.';
}
The correct answer is:
$roles = ['admin', 'manager'];
$needle = ['manager', 'admin'];
sort($roles);
sort($needle);
if ($roles === $needle) {
echo 'The same.';
}
== with arrays evaluates to true if $a and $b have the same key/value pairs. In your example, the numeric keys 0 and 1 match up with different values, so the comparison fails.
As other people have mentioned, you can use array_diff to compare just the values, e.g.
if (count(array_diff($needle, $roles)) > 0) {
// Arrays are not identical
}
but note that array_diff only works in one direction - it returns the values from the first argument that are not present in the second, so you might also need to run it with the arguments in reverse order, depending on your exact scenario.
As #axiac mentions in the comment, this still won't cover situations where elements can occur multiple times. If this applies then there are probably better ways that you can approach this, e.g. by sorting the arrays or making use of functions like array_intersect.
Use the array_diff function to return any values from the second array that are not in the first. If array_diff is empty then the arrays are the same:
$roles = ['admin', 'manager'];
$needle = ['manager', 'admin'];
$result = array_diff($roles, $needle);
if (empty($result)) {
// Arrays are the same
}
I want to verify if a levenshtein of factor <= 2 is present in an array. So:
in_array("test", $some_array);
to something like "check if in array, can have errors if levenshtein factor <= 2, by comparison"
levenshtein("test", $element_of_array_by_'in_array'_function);
Is this possible or do I have to iterate the array?
This should work for you:
You are looking for array_reduce(). With this you can reduce your array to one single return value.
You start with FALSE as return value. Then you loop through each array element and check if the return value of levenshtein() is smaller or equals 2.
If not then your return value of array_reduce() won't change and is still FALSE. If it is smaller or equals 2 you change the value to TRUE and array_reduce() will return TRUE.
array_reduce($some_array, function($keep, $v){
if(levenshtein($v, "test") <= 2)
return $keep = TRUE;
return $keep;
}, FALSE);
I want to know how to array_intersect for object array.
You can use array_uintersect in conjunction with spl_object_hash, see an example:
array_uintersect($a, $b, function($a, $b) {
return strcmp(spl_object_hash($a), spl_object_hash($b));
});
where '$a' and '$b' are arrays of some objects that you want to intersect.
nice toString function is already implemented and is called serialize ;) so
array_map(
'unserialize',
array_intersect(
array_map(
'serialize',
$obj1
),
array_map(
'serialize',
$obj2
)
)
);
will do the work, example mentioned higher don't work 'cause array_intersect work's only with strings as someone mentioned too
array_intersect() returns an array containing all the values of array1 that are present in all the arguments.
Then what mean present in this context (exacly this function), i found on php.net my answer:
Two elements are considered equal if
and only if (string) $elem1 ===
(string) $elem2. In words: when the
string representation is the
same.
Then you can't use it on array of objects if your objects not implements unique conversion to string.
Had a similar problem a few days ago, while these are answers are on the right path; I used them to work out the following:
From Artefacto's answer return $obj1 == $obj2 didn't really work, so I wrote a simple comparative function (basically gets the md5 of the serialised object and compares that):
function object_compare($obj1, $obj2){
$md5 = function($obj){
return md5(serialize($obj));
};
return strcmp($md5($obj1), $md5($obj2));
}
Then it’s jut a matter of calling array_uintersect with our comparative function to get the intersection:
# $array1 / $array2 are the array of objects we want to compare
return array_uintersect($array1, $array2, 'object_compare');
In my case, I had an unknown / dynamic array of objects, so I took it a step further so I don't have to declare array_uintersect($array1, $array2, ...) specifically - but just be able to pass in an array of arrays (of objects):
# $multiarray_of_objects is our array of arrays
$multiarray_of_objects[] = 'object_compare';
return call_user_func_array('array_uintersect', $multiarray_of_objects);
Just gotta remember to pass in the reference to our callback / comparative function as the last string in the array. Works like a charm!
The correct way to check whether two objects are equal is to use ==. Therefore:
array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 == $a2; });
Just for completeness: Implement __toString() method in your object returning a unique value. For database entities this might be as easy as returning the fully qualified class name postfixed with the ID of the record. But it can also be arbitrarily complex by doing some hashing or even worse things.
In my opinion, it's the class's duty to serialize itself or create something unique to compare its objects by. Using anything outside of a class to serialize an object might result in strange behaviour (including comparing objects of different classes, which must never result in equality).
I use array_udiff to implement array_intersect for an object array.
function diff($a, $b) {
if($a === $b) {
return 0;
} else {
return 1;}
}
$array_1 = array('a', 'b', 'c');
$array_2 = array('c', 'd','e');
$array = array_udiff($array_1, array_udiff($array_1, $array_2, 'diff'),'diff');
var_dump($array);
return array(1) { [2]=> string(1) "c" }
You can have your own diff function for any scheme.
The correct solution would be:
array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 != $a2; });
Note the != in the callback function as opposed to the answer from #Artefacto. Based on the documentation of array_uintersect, the callback function has to return 0 (false) if array items are equal.
I have 2 variables each containing a number (integer). I would like to sort them to have the lowest number first and the second largest. For example:
$sortedVar = getSmaller(45, 62); // Will return 45
$sortedVar = getSmaller(87, 23); // Will return 23
Do you see what I want to do? Can you help me please?
Thanks :)
http://php.net/manual/en/function.min.php
min — Find lowest value..
If the first and only parameter is an array, min() returns the lowest value in that array. If at least two parameters are provided, min() returns the smallest of these values.
Note:
Values of different types will be compared using the standard comparison rules. For instance, a non-numeric string will be compared to an integer as though it were 0, but multiple non-numeric string values will be compared alphanumerically. The actual value returned will be of the original type with no conversion applied.
Caution
Be careful when passing arguments with mixed types values because min() can produce unpredictable results...
Use min() which supports any number of arguments as well as arrays.
$smallest = min(1,2); //returns 1
$smallest = min(4,3,2); //returns 2
$smallest = min(array(5,4)) //returns 4
function getSmaller($a, $b) {
return $a < $b ? $a : $b;
}
In plain english, if $a is smaller than $b, then return $a, else return $b.
Or as others pointed out, there's also a function for that, called min().
$sortedVar = $a < $b ? $a : $b;