Can someone please explain to me whats the difference between these 2 functions:
array_diff_ukey
array_diff_uassoc
They both take keys into the compare function, and based on those keys decide if the array element should be returned or not. I've checked the php manual, but to me they both seem to be doing the same thing...
array_diff_ukey returns those elements of the first array whose keys compare different to all keys in the second array (the semantics of the comparison being user-defined). The values associated with those keys do not play a part.
array_diff_uassoc is a "more inclusive" version of the above that also checks values: if a key in the first array compares equal to a key in the second array but the values are different, that element is also included in the result. In this case, comparison of values is not user-defined, but works as in array_diff: for two values to compare equal, their string representation must be identical.
Example, adapted from the PHP docs:
function key_compare_func($key1, $key2)
{
if ($key1 == $key2)
return 0;
else if ($key1 > $key2)
return 1;
else
return -1;
}
$array1 = array('blue' => 1, 'red' => 2, 'green' => "3", 'purple' => 4);
$array2 = array('green' => 3, 'blue' => 6, 'yellow' => 7, 'cyan' => 8);
var_dump(array_diff_ukey($array1, $array2, 'key_compare_func'));
var_dump(array_diff_uassoc($array1, $array2, 'key_compare_func'));
See it in action.
Here, array_diff_ukey will return the "red" and "purple" elements from $array1 because these keys do not compare equal to any of the keys in $array2. However array_diff_uassoc will also return the "blue" element, because even though that key exists in both arrays the associated values are different.
Note that the "green" element is not included in either result, despite the fact that the associated value is a string in $array1 and an integer in $array2.
From the manual:
array_diff — Computes the difference of arrays
array_diff_key — array_diff_key — Computes the difference of arrays using keys for comparison
array_diff_assoc — Computes the difference of arrays with additional index check
This additional index check means that not only the value must be the same, but also the key must be the same. So the difference between array_diff_ukey and array_diff_uassoc is that the latter checks both keys and values, while the first only checks the keys.
The addition of the u after diff_ means that you must supply a custom callback function instead of the default built-in function.
Example based on the manual (Fiddle)
<?php
header("Content-Type: text/plain");
$array1 = array('blue' => 1, 'red' => 2, 'green' => 3, 'black' => 0, 'purple' => 4);
$array2 = array('green' => 5, 'blue' => 6, 'yellow' => 7, 'cyan' => 8, 'black' => 0);
var_dump(array_diff($array1, $array2));
var_dump(array_diff_key($array1, $array2));
var_dump(array_diff_assoc($array1, $array2));
?>
Output
array(4) {
["blue"]=>
int(1)
["red"]=>
int(2)
["green"]=>
int(3)
["purple"]=>
int(4)
}
array(2) {
["red"]=>
int(2)
["purple"]=>
int(4)
}
array(4) {
["blue"]=>
int(1)
["red"]=>
int(2)
["green"]=>
int(3)
["purple"]=>
int(4)
}
Related
The documentation for min() shows the following example:
// Multiple arrays of the same length are compared from left to right
// so in our example: 2 == 2, but 4 < 5
$val = min(array(2, 4, 8), array(2, 5, 1)); // array(2, 4, 8)
Given the following code:
$input = [
[3, 6],
[2, 9],
];
var_dump(min(...$input)); // returns [2, 9] as expected
If you make the same array associative, it fails and always seems to just return the first array:
$input = [
["three" => 3, "six" => 6],
["two" => 2, "nine" => 9],
];
var_dump(min(...$input)); // returns ["three" => 3, "six" => 6]
Why?
Here's an example
According to the documentation values are compared using the standard comparison rules.
In the table of "comparison with various types" there, it states that if both operands are arrays and a key in operand 1 is not present in operand 2, then the arrays are not comparable. This is why min simply returns whatever is the first value in your array.
Specifically, arrays are compared as follows:
If one array has fewer values than the other, it is smaller.
Otherwise if any key in operand 1 is not in operand 2, the arrays are uncomparable.
For each value in operand 1 in turn, compare with the value with the same key in operand 2.
If the same, continue comparing values.
Otherwise the smaller array is the one with the smaller value.
Because they are not comparable, min is simply returning the first array in the list. If you swap the order, the other array will be returned. You can see this if you sort the array with sort($input). Every time you sort it the array is reversed.
To get the behaviour you desire, sort the arrays based on their values and then fetch the first element. But be aware that this will depend which key you defined first, so ["three" => 3, "six" => 6] is not the same as ["six" => 6, "three" => 3].
usort($input, function($a, $b) { return array_values($a) <=> array_values($b); });
var_dump($input[0]);
Just convert them to simple arrays and then return the associative array associated to the result of min.
<?php
function array_min_assoc(){
$args = func_get_args();
$not_assoc = array_map('array_values',$args);
$min = min(...$not_assoc);
$key = array_search($min, $not_assoc);
if ($key !== false){
return $args[$key];
}
}
$input = [
["three" => 3, "six" => 6],
["two" => 2, "nine" => 9],
];
var_dump(array_min_assoc(...$input));
/* returns
array(2) {
["two"] => int(2)
["nine"]=> int(9)
}
*/
dear users of StackOverflow. There is some problem.
Array 1:
array: 3 [▼
0 => "8"
1 => "13"
2 => "15"
]
Array 2:
array: 16 [▼
0 => 7
1 => 8
2 => 9
]
array_diff does not work, because in the first number, in the second string.
Please suggest any ideas for solving the issue. I will be happy with any comment. Many thanks.
You can use array_udiff to compare the arrays using a user-defined callback which converts the values to ints before comparing:
$array1 = array('8', '13', '15');
$array2 = array(7, 8, 9);
$diffs = array_udiff($array1, $array2, function ($a, $b) { return (int)$a - (int)$b; });
print_r($diffs);
Output:
Array
(
[1] => 13
[2] => 15
)
Update
It has been pointed out that the desired output hasn't been specified, so here is how to get all unique values:
$diffs1 = array_udiff($array1, $array2, function ($a, $b) { return (int)$a - (int)$b; });
$diffs2 = array_udiff($array2, $array1, function ($a, $b) { return (int)$a - (int)$b; });
$diffs = array_merge($diffs1, $diffs2);
print_r($diffs);
Output:
Array
(
[0] => 13
[1] => 15
[2] => 7
[3] => 9
)
and all matching values using array_uintersect:
$same = array_uintersect($array1, $array2, function ($a, $b) { return (int)$a - (int)$b; });
print_r($same);
Output:
Array
(
[0] => 8
)
Note
In PHP7 there is now the spaceship operator (<=>) which can also be used in the comparison function e.g.
$diffs = array_udiff($array1, $array2, function ($a, $b) { return (int)$a <=> (int)$b; });
You could convert the arrays using array map like this
$a1 = array_map('intval', $a1);
$a2 = array_map('intval', $a2);
Then do your array diff and what not.
#Nick's solution is a bit more elegant.
Because, it's not walking the arrays 2x more then you really need to. Of course if you know which is string then you could just convert that one, but I thought I would post another way to do it...
For testing you can simply do this
$a = [
"1",
"2" ,
"3"
];
var_dump($a);
var_dump(array_map('intval', $a));
Output
array(3) {
[0]=> string(1) "1"
[1]=> string(1) "2"
[2]=> string(1) "3"
}
array(3) {
[0]=> int(1)
[1]=> int(2)
[2]=> int(3)
}
Sandbox
And this shows that it does convert the values to string, which was pretty obvious, but I like examples. So there you go.
Cheers!
UPDATE
After doing some simple bench marking, with an array of 100,000 string numbers, and taking the average time from 100 iterations, it took apx 0.0072/seconds to convert the array back to ints:
//setup
$a = array_map('strval', range(0, 100000));
//get microtime as float after setup
$time = microtime(true);
//use the average of 100 conversion for consistency
$iterations = 100;
for($i=0;$i<$iterations; ++$i){
$b = array_map('intval', $a); //set to $b so we don't convert $a on our first iteration.
//check the first iteration, to make sure we have it setup correctly for our averaging
if($i==0)
echo number_format(
((microtime(true) - $time)),
4
)." \seconds\n\n";
}
echo number_format(
((microtime(true) - $time) / $itterations),
4
)." \seconds\n";
Output
0.0067 \seconds
//if these are close (which they are) we have everything setup right,
//it's better to take the average for speed testing.
//and I just wanted to double check that it was being done correctly
0.0072 \seconds
Sandbox
-note- the sandbox has only 134M of Ram for PHP (i've run it out of memory on purpose to test it.. lol)
<b>Fatal error</b>: Allowed memory size of 134217728 bytes exhausted
UPDATE1
It has been pointed out that the desired output hasn't been specified, so here is how to get all unique values:
If you want the Unique values from both arrays you can do
$unique = array_unique(array_replace($a1,$a2));
And if the arrays are unique beforehand you can just do array_replace because you will be combining 2 unique arrays replacing any in the one array that are duplicated in the other. Therefore the result will be the unique combination of 2 unique arrays, if that makes sense.
This is a non-issue. PHP DOES NOT require a workaround for string numbers versus non-string numbers.
Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In other words: when the string representation is the same.
Source: https://www.php.net/manual/en/function.array-diff.php
Here's my output in PHP 7.4.7. (The same output happens when I run it in PHP 5.6, and that's the earliest version I have installed right now.)
php > var_dump(array_diff([1,2,3], ["2"]));
array(2) {
[0]=>
int(1)
[2]=>
int(3)
}
$array1 = [
'1' => '11',
'b' => 1,
3 => 33,
8 => 8
];
$array2 = [
'1' => '22',
'b' => 2,
3 => 44,
9 => 9
];
$merged = array_merge_recursive($array1, $array2);
and the result is:
array(7) {
[0]=>
string(2) "11"
["b"]=>
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}
[1]=>
int(33)
[2]=>
int(8)
[3]=>
string(2) "22"
[4]=>
int(44)
[5]=>
int(9)
}
so lets take a glance: the only part is the 'b' keys, they are actually works. I dont want to overwrite anything of it but putting them together to an array. Thats good! But then keys the other numeric keys (int or string) are screwed up.
I want to have this as result:
[
'1' => ['11', '22']
'b' => [1, 2]
[3] => [33, 44]
[8] => 8,
[9] => 9
]
possible?
EDIT: of course keys "1" and 1 - string vs int key are the same
Let's break down this question into to separate problems:
When a key in the second array exist in the first array, you want to create an array and make the value the first element of that array.
To be honest, I don't know an easy way of solving this. I'm not sure there is one. And even if, I'm not sure you really want it. Such a function will lead to arrays having values that are a string or an array. How would you handle such an array?
Update: Well, there is one. Your example already shows that array_merge_recursive will convert values with a string key into an array. So 1 => 'foo' would be 0 => 'foo', but 'foo' => 'bar' will end up as 'foo' => ['bar']. I really don't understand this behaviour.
Using string keys would help you out in this case, but after learning more about array_merge_recursive I decided to avoid this function when possible. After I asked this question someone filed it as a bug in it since PHP 7.0, since it works differently in PHP 5.x.
You want to keep the keys, while array_merge_resursive doesn't preserve integer keys, while it does for integer keys:
If the input arrays have the same string keys, then the values for
these keys are merged together into an array, and this is done
recursively, so that if one of the values is an array itself, the
function will merge it with a corresponding entry in another array
too. If, however, the arrays have the same numeric key, the later
value will not overwrite the original value, but will be appended.
To make it worse, it handles differently when handling the nested arrays:
$array1 = [30 => [500 => 'foo', 600 => 'bar']];
$array2 = [];
array_merge_recursive($array1, $array2);
//[0 => [500=> 'foo', 600 => 'bar']];
So effectively, array_merge_resursive isn't really resursive.
Using array_replace_resursive solves that problem:
array_replace_recursive($array1, $array2);
//output:
//array:5 [
// 1 => "22"
// "b" => 2
// 3 => 44
// 8 => 8
// 9 => 9
//]
Please note that PHP is very consistent in being inconsistent. While array_merge_recursive isn't recursive, array_replace_recursive doesn't replace (it also appends):
If the key exists in the second array, and not the first, it will be
created in the first array.
In many cases this is desired behavior and since naming functions isn't PHP's strongest point, you can consider this as a minor issue.
Can you rely on a native function to return your exact desired output? No. At least not in any version as of the date of this post (upto PHP8.1).
The good news is that crafting your own solution is very simple.
Code: (Demo)
foreach ($array2 as $key => $value) {
if (!key_exists($key, $array1)) {
$array1[$key] = $value;
} else {
$array1[$key] = (array)$array1[$key];
$array1[$key][] = $value;
}
}
var_export($array1);
I suppose I am less inclined to recommend this output structure because you have potentially different datatypes on a given level. If you were building subsequent code to iterate this data, you'd need to write conditions on every level to see if the data was iterable -- it just feels like you are setting yourself up for code bloat/convolution. I'd prefer a result which has consistent depth and datatypes.
how do I merge two arrays, and just increase the values when the keys match?
Both the arrays might contain keys that does not exists in the other, and both of them might have more or less keys than the other.
So I cannot just take one array, and loop trough that and compare with the other since some times the "other" array might have more values. This swithces from time to time.
Array merge doesn't seem to be working either since it overwrites the old value.
I need to return all unique keys from both arrays, but but whenever a key matches, I need to just add the two values together.
Array_a:
["BMW-B2"]=>int(1)
["BMW-N-115 "]=>int(5)
["BMW-N-143"]=>int(3)
["BMW-N-163"]=>int(10)
["BMW-N-184"]=>int(4)
Array_b
["CR220036"]=>int(3)
["BMW-N-163"]=>int(9)
["CR220822"]=>int(7)
Array_merged
["BMW-B2"]=>int(1)
["CR220036"]=>int(3)
["CR220822"]=>int(7)
["BMW-N-115 "]=>int(5)
["BMW-N-143"]=>int(3)
["BMW-N-163"]=>int(19) // <-- this has increast by 9
["BMW-N-184"]=>int(4)
The order is not important.
You can achieve this by using a loop and and merging the arrays. Basically, you pick one array that you will check for duplicate values, perform your arithmetic, unset the no longer needed array elements, and then merge it all together.
Consider adapting your code similar to the following:
$array1 = array("val1" => 5, "val2" => 8, "val3" => 10, "val4" => 2, "val5" => 12);
$array2 = array("val3" => 9, "val6" => 11, "val1" => 15);
foreach ($array2 as $key => $value)
{
if (array_key_exists($key, $array1))
{
$array1[$key] += $value;
unset($array2[$key]);
}
}
$merged = array_merge($array1, $array2);
var_dump($merged);
//Output:
array(6) {
["val1"]=>
int(20)
["val2"]=>
int(8)
["val3"]=>
int(19)
["val4"]=>
int(2)
["val5"]=>
int(12)
["val6"]=>
int(11)
}
In my example, all the values in $array2 are compared with $array1 to locate duplicate keys and increment by the stored value. If a dupe is found then that element is unset, this will prevent the modified keys in $array1 from being overwritten in the array_merge() operation.
How about this:
$arr1=array(1,2,3,4);
$arr2=array(1,2,5=>1);
$result=array();
foreach($arr1 as $k=>$v){
$result[$k]=$v;
if(isset($arr2[$k])){
$result[$k]+=$arr2[$k];
}
}
foreach($arr2 as $k=>$v){
if(!isset($result[$k]){
$result[$k]=$v;
}
}
var_dump($result);
I am new to associative array concept of php. I had never used associative array before this. I came through an example, and got the following code:
function isAssoc($arr)
{
return array_keys($arr) !== range(0, count($arr) - 1);
}
echo(var_dump(isAssoc(array("0" => 'a', "1" => 'b', "2" => 'c'))).'<br />'); // false
echo(var_dump(isAssoc(array("1" => 'a', "1" => 'b', "2" => 'c'))).'<br />'); //true
echo(var_dump(isAssoc(array("1" => 'a', "0" => 'b', "2" => 'c'))).'<br />'); // true
echo(var_dump(isAssoc(array("a" => 'a', "b" => 'b', "c" => 'c'))).'<br />'); // true
The above function is used to tell whether the array is associate array or not.
I have this doubt why:
array("0" => 'a', "1" => 'b', "2" => 'c')
is not an associative array as it returns false. Whereas,
array("1" => 'a', "0" => 'b', "2" => 'c') //OR
array("1" => 'a', "1" => 'b', "2" => 'c')
is an associative array?
The term "associative array" in the PHP manual is used to differentiate from "indexed array". In PHP, all arrays are by definition associative in that they contain key/value pairs (the association). However, the documentation aims to refer to "associative" when it means "non-indexed", for clarity. This is the important point.
So what is an "indexed array"? By "indexed", it is meant that the keys of the array are starting at 0 and incrementing by one. Whether the keys are explicitly set at definition (array(0 => 'a', 1 => 'b')) or implicit (array('a', 'b')) makes no difference. Any other sequence of keys would be referred to as "associative".
Note that how the PHP manual uses the term "associative" doesn't necessarily correlate precisely with the same term's use elsewhere.
All arrays in PHP are associative, you can consider it to be tuple if all keys are numeric (integers but not necessarily of that type), continuous and start from 0.
Simple check would be:
function is_assoc(array $array)
{
$keys = array_keys($array);
$keys_keys = array_keys($keys);
return $keys_keys !== $keys;
}
It would yield same results as the one you've linked to/used.
A hint here would be excerpt from json_decode documentation:
assoc
When TRUE, returned objects will be converted into associative arrays.
Even if it returns "numeric" and "indexed" array it's still associative.
Another example would be:
$array = ["0" => "a", "1" => "b", "2" => "c"]; # numeric, continuous, starts from 0
json_encode($array); # (array) ["a","b","c"]
While:
$array = ["0" => "a", "2" => "b", "3" => "c"]; # numeric, NOT continuous, starts from 0
json_encode($array); # (list) {"0":"a","2":"b","3":"c"}
The function you're referring to has flaws and it is not authoritative about the fact whether or not an array in PHP is associative or not.
In-fact, in PHP it is not strictly defined what the term associative array actually means.
However it is important to understand that
PHP is loosely typed
PHP differs in array keys between integer (bool, NULL, float, integer, numeric string represented as integer) and string (nun-numeric strings) for keys.
Most likely an associative array in PHP is one where inside the array (that is after creating it, not while it seems when writing the array definition) that all keys in that array are strings.
But keep in mind that no set-in-stone definition exists. PHP arrays are just a mixture of array and hash-map where you need to know both without having both properly differentiated and the need to keep in mind the difference between numeric and non-numeric keys and how the loosely type-system of PHP converts for the array key needs.
First page to go as usual:
http://php.net/language.types.array
For those who really can only understand it with code, here the pony goes:
function is_array_assoc(array $array) {
return is_array($array);
}
If you can ensure that you're not using an error-handler that catches catchable errors, then the following code is correct, too:
function is_array_assoc(array $array) {
return TRUE;
}
The later example makes it perhaps a bit more clear that all arrays in PHP are associative.