why does array_intersect_uassoc() compare array keys within the same array? - php

Here's an example of using array_intersect_uassoc().
I've deliberately made the keys different, so you can tell which one comes from which array.
$array1 = array("LEFT a" => "green", "LEFT b" => "brown", "LEFT c" => "blue");
$array2 = array("RIGHT a" => "GREEN", "RIGHT B" => "brown", 'RIGHT y' => "yellow");
array_intersect_uassoc($array1, $array2, function($a, $b) {
print_r("$a -- $b");
return 0;
});
In the output, I see comparisons of two LEFT keys as well as two RIGHT keys.
What on earth is the point of doing this?

Looking at the logic code for the function (I simplified it a bit):
function array_intersect_uassoc($args, compare_func)
{
$result = array();
$array_count = count($args);
foreach ($args[0] as $k => $v) {
for ($i = 0; $i < $array_count; $i++) {
$match = false;
foreach ($args[$i] as $kk => $vv) {
$compare = call_user_func_array($compare_func, array($k, $kk));
if ($compare === 0 && $v == $vv) {
$match = true;
continue 2;
}
}
if ($match === false) {
continue 2;
}
}
if ($match === true) {
$result[$k] = $v;
}
}
return $result;
}
This helps you see that the keys are not the only thing checked, but more importantly the values as well.

Related

How to return elements of an array with the highest values?

In my code I have two elements which has the same age "Joe"=>"43" and "Rob"=>"43" .
My code output is:
Joe
I want to output:
Joe and Rob
Because they has the highest value.
This is my code:
<?php
$cur = 1;
$age = array("Peter" => "35", "Ben" => "37", "Joe" => "43", "Rob" => "43");
$new_array = array();
arsort($age);
$new_array = $age;
$key = array_search(max($new_array), $new_array);
while ($cur > 0) {
echo $key;
$cur--;
}
?>
I'd change the keys and values in the array, then sort by key and return the values of the first key:
$ages = array("Peter" => "35", "Ben" => "37", "Joe" => "43", "Rob" => "43");
$new = array();
foreach ($ages as $name => $age) {
$new[$age][] = $name;
}
uksort($new, function($ka, $kb) { return $kb - $ka; }); // or just krsort($new);
$new = array_values($new)[0]; // <- to use this you have to have at least PHP 5.4
// if you have PHP < 5.4 you can simply do it in two steps:
// $new = array_values($new);
// $new = $new[0];
See it in action!
EDIT: even simpler!
$ages = array("Peter" => "35", "Ben" => "37", "Joe" => "43", "Rob" => "43");
$max = max($ages);
$new = array_keys(array_filter($ages, function ($age) use ($max) { return $age == $max; }));
Use:
$people = array("Peter" => "35", "Ben" => "37", "Joe" => "43", "Rob" => "43");
$max = max($people);
$result = array_filter($people, function($age) use ($max){ return $max == $age; });
The result is:
Array
(
[Joe] => 43
[Rob] => 43
)
Just check it manually:
$age = array("Peter" => "35", "Ben" => "37", "Joe" => "43", "Rob" => "43");
$new_array = array();
arsort($age);
$new_array = $age;
$max = max($new_array);
$results = array();
foreach ($new_array as $key => $val) {
if ($val == $max) {
$results[] = $key;
}
}
echo implode(' and ', $results);
// will output "joe and bob"
I like the answer of #matteo-tassinari and #evilive much more and wanted to propose it myself. But since the question of efficency came up, here is a solution using only one loop and therefore has a linear time complexity:
<?php
$max = ~PHP_INT_MAX;
$result = [];
foreach($age as $key => $value) {
if($value > $max) {
$result = [ $key => $value ];
$max = $value;
}
if($value == $max) {
$result[$key] = $value;
}
}
var_dump($result);
And there exists another solution, that uses bucket sort:
function bucket($ages) {
$buckets = [];
foreach($ages as $key => $value) {
$buckets[$value][] = $key;
}
return $buckets[max(array_keys($buckets))];
}
Regarding the discusson about peformance and scalability, I wrote a small benchmark script for four kinds of proposed solutions (loop, sort, filter, bucket):
<?php
function loop($ages) {
$max = 0;
$result = [];
foreach($ages as $key => $value) {
if($value > $max) {
$result = [ $key => $value ];
$max = $value;
}
if($value == $max) {
$result[$key] = $value;
}
}
return array_keys($result);
}
function filter($ages) {
$max = max($ages);
$new = array_filter($ages, function ($age) use ($max) { return $age == $max; });
return array_keys($new);
}
function bucket($ages) {
$buckets = [];
foreach($ages as $key => $value) {
$buckets[$value][] = $key;
}
return $buckets[max(array_keys($buckets))];
}
for($n = 2; $n < 10000000; $n*=2) {
$ages = [];
for($i = 0; $i < $n; $i++) {
$ages['name_'.$i] = rand(0,100);
}
$start = microtime(true);
echo $n.';';
loop($ages);
echo (microtime(true) - $start).';';
$start = microtime(true);
arsort($ages);
echo (microtime(true) - $start).';';
$start = microtime(true);
filter($ages);
echo (microtime(true) - $start).';';
bucket($ages);
echo (microtime(true) - $start).';';
echo PHP_EOL;
}
Limited Live Test
Please double-check if this is right: Using php-5.6.15 on the command line, my timings look something like this:
elements;loop;sort;filter;bucket
...
4096;0.001507;0.009868;0.01211;0.01453;
8192;0.003704;0.002483;0.02488;0.03035;
16384;0.006660;0.01010;0.05396;0.06723;
32768;0.01417;0.01271;0.09163;0.1163;
...
1048576;0.4227;0.9791;2.983;3.943;
2097152;0.8572;2.320;6.064;8.020;
4194304;1.787;4.981;11.90;16.04;
8388608;3.892;10.84;24.76;33.31;
For small number of elements, the difference between the methods is not really big, but as you can see, for the largest value the loop method is two times faster than sort, 8 times faster than filter and eleven times faster than bucket. So if your array is huge, you should use loop.
I'd do something like this
$age=array("Peter"=>"35","Ben"=>"37","Joe"=>"43","Rob"=>"43");
$max = max($age); //get the highest age
foreach ($age as $key => $value) { //iterate through $age array
if ($value == $max) { //if the value is equal to max age
echo $key."<br />"; // then echo the key
}
}
You can use the array methods next and key.
With next() you will move the array pointer one position. With key() you will get the key of the element of the array pointer. So the final code will be something like this:
$age=array("Peter"=>"35","Ben"=>"37","Joe"=>"43","Rob"=>"43");
arsort($age);
echo key($age);
next($age);
echo key($age);
Check it working here.
I'm wondering why no one is using built in solution:
$age = array("Peter"=>"35","Ben"=>"37","Joe"=>"43","Rob"=>"43");
$new = array_keys($age, max($age));
returns
array('Joe', 'Rob')
https://www.php.net/manual/en/function.array-keys.php says:
array_keys ( array $array , mixed $search_value [, bool $strict = FALSE ] ) : array
If a search_value is specified, then only the keys for that value are returned. Otherwise, all the keys from the array are returned.

Compare two associative arrays regarding the order of keys

Suppose, we have 2 associative arrays:
<?php
$array1 = array(
1 => 2,
2 => 1,
);
$array2 = array(
2 => 1,
1 => 2,
);
They contain same elements, but in different order. I wanted to write a comparison function, that will give true only if:
Arrays have the same key => value pairs.
Order of pairs is the same.
So, I tried the following:
1 try
if ($array1 == $array2)
{
print "equal\n";
}
Prints: equal
2 try
print count(array_diff_assoc($array1, $array1));
Prints: 0
My custom function
Then I created the following function:
function compare(&$array1, &$array2)
{
$n1 = count($array1);
$n2 = count($array2);
if ($n1 == $n2)
{
$keys1 = array_keys($array1);
$keys2 = array_keys($array2);
for ($i = 0; $i < $n1; ++$i)
{
if ($keys1[$i] == $keys2[$i])
{
if ($array1[$keys1[$i]] != $array2[$keys2[$i]])
{
return false;
}
}
else
{
return false;
}
}
return true;
}
else
{
return false;
}
}
This works correctly, but it won't work, when we want this strict comparison applied for nested arrays of arrays because of != operator in this if:
if ($array1[$keys1[$i]] != $array2[$keys2[$i]])
{
return false;
}
This can be fixed, using a recursive function, switching by type of data. But this simplified version was ok for me.
Is there any standard solution for this type of comparison?
As described under array operators, what you want is the === equality operator.
if ($array1 === $array2) {
echo "same key pairs and same element order\n";
}
can you try the this below one. which returns true
if the key=>value and will be in any order
otherwise returns false
$array1 = array(
1 => 2,
2 => 1);
$array2 = array(
2 => 1,
1 => 2);
var_dump(compareArray($array1,$array2));
function compareArray ($array1, $array2)
{
foreach ($array1 as $key => $value)
{
if ($array2[$key] != $value)
{
return false;
}
}
return true;
}

Unsort in array

Good day.
I would be like get 3 keys from $arr where value $arN[0] will be more than other...
Code:
$ar1=array(201,281);
$ar2=array(1252760,1359724);
$ar3=array(452760,34349724);
$ar4=array(1260,134344);
$ar5=array(232750,1359724);
$ar6=array(60,1439724);
$arr[]=array(6299927 => $ar1);
$arr[]=array(1252760 => $ar2);
$arr[]=array(3432444 => $ar3);
$arr[]=array(3435543 => $ar4);
$arr[]=array(7645466 => $ar5);
$arr[]=array(4574534 => $ar6);
Next function sorting array $a descending:
function cmp($a, $b)
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? 1 : -1;
}
$a = array(3, 2, 5, 6, 1);
usort($, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
outpoot: 0:6 1:5 2:4 3:2 4:1
But how change this function for my example(for my big array)?
Tell me please how make this?
How write right?
Very confusing but I think you are looking for something like this. It will give you the index of the array whose first item is greater than the first item in the next array.
foreach($arr as $key => $array) {
if($array[0] > $arr[$key+1][0]) {
echo $key;
}
}
$dates = array();
foreach ($arr as $key => $row) {
$subkeys = array_keys($row); // Elements are one-element associative arrays
$dates[$key] = $row[$subkeys[0]][0]; // Get the element, then get its [0] sub-element
}
array_multisort($dates, SORT_DESC, $arr);
print_r($arr);
$ans_array= array();
// for shuffle array without repeat
for($q=0;$q<count($arr);$q++)
{
$n = rand(0,count($arr));
if($ans_array[$n] == NULL || $ans_array[$n] == '')
{
$ans_array[$n] = $arr[$q];
}
else
{ if($q==0) $q=0;
else $q = $q-1;
}
}

PHP array comparing

I want to compare two arrays in php. I don't want to do it overall, but block by block.
kind of like this
if (a[1] == b[1]){ // do something }
if (a[2] == b[2]){ // do more }
how can i do this without a whole bunch of ifs ?
thanks in advance :)
$a = array(1, 2, 3, 5);
$b = array(1, 1, 1, 1);
$c = array('something', 'something', 'and so forth');
foreach($a as $key => $value){
if($value == $b[$key]){
echo $c[$key]. '<br />';
}
}
my answer. compare 2 arrays, then rune some code. triggered by the blocks that match
for($i=0;$i<sizeof(a);$i++){
if(a[$i]==b[$i]){
//DO SOMETHING
}
}
want to compare whole array element one by one (assuming both array of same length)
foreach($a as $key => $value){
if($value == $b[$key])
{
// do something
}
else
{
break; // stop doing something and break
}
}
if want to compare some keys
$keys = array('key1', 'key2');
foreach($keys as $value){
if($a[$value] == $b[$value])
{
// true
}
else
{
// false
}
}
$a = array(1, 3 , 5 ,6 , 7);
$b = array(3, 1, 5, 6, 8 ,9);
$array_size = min(count($a), count($b));
for ($i = 0; $i < $array_size; $i++) {
if ($a[$i] == $b[$i]) { //you could/should check whether the index is present.
//some code
}
}
This only works for arrays with the same uniformly distributed numerical index.
foreach(array_intersect_assoc($a,$b) as $key => $data)){
switch($key){
case 1:
//something
break;
case 2:
//something
break;
}
}
for ($i=0; $i < count($a) && $i < count($b); ++$i) {
if ($a[$i] == $b[$i]){
// this is true
} else {
// this is false
}
}
A good ol for loop should do the trick. You can start with an array of things to do:
$arrayOfThingsToDo = array( "someFunc", "anotherFunc", "yetAnotherFunc" );
$arrayOfA = array( "one", "two", "three" );
$arrayOfB = array( "one", "not two", "three" );
function doCompare($a, $b, $f) {
$len = count($a);
for($i = 0; $i < $len; $i++) {
if($a[$i] == $b[$i]) {
$f[$i]();
}
}
}
Good luck!

How does array_diff work?

How does array_diff() work? It obviously couldn't work as follows:
function array_diff($arraya, $arrayb)
{
$diffs = array();
foreach ($arraya as $keya => $valuea)
{
$equaltag = 0;
foreach ($arrayb as $valueb)
{
if ($valuea == $valueb)
{
$equaltag =1;
break;
}
}
if ($equaltag == o)
{
$diffs[$keya]=$valuea;
}
}
return $diffs;
} //couldn't be worse than this
Does anyone know a better solution?
EDIT #animuson:
function array_diff($arraya, $arrayb)
{
foreach ($arraya as $keya => $valuea)
{
if (in_array($valuea, $arrayb))
{
unset($arraya[$keya]);
}
}
return $arraya;
}
user187291's suggestion to do it in PHP via hash tables is simply great! In a rush of adrenaline taken from this phantastic idea, I even found a way to speed it up a little more (PHP 5.3.1):
function leo_array_diff($a, $b) {
$map = array();
foreach($a as $val) $map[$val] = 1;
foreach($b as $val) unset($map[$val]);
return array_keys($map);
}
With the benchmark taken from user187291's posting:
LEO=0.0322 leo_array_diff()
ME =0.1308 my_array_diff()
YOU=4.5051 your_array_diff()
PHP=45.7114 array_diff()
The array_diff() performance lag is evident even at 100 entries per array.
Note: This solution implies that the elements in the first array are unique (or they will become unique). This is typical for a hash solution.
Note: The solution does not preserve indices. Assign the original index to $map and finally use array_flip() to preserve keys.
function array_diff_pk($a, $b) {
$map = array_flip($a);
foreach($b as $val) unset($map[$val]);
return array_flip($map);
}
PS: I found this while looking for some array_diff() paradoxon: array_diff() took three times longer for practically the same task if used twice in the script.
UPDATE
see below for faster/better code.
array_diff behaviour is much better in php 5.3.4, but still ~10 times slower than Leo's function.
also it's worth noting that these functions are not strictly equivalent to array_diff since they don't maintain array keys, i.e. my_array_diff(x,y) == array_values(array_diff(x,y)).
/UPDATE
A better solution is to use hash maps
function my_array_diff($a, $b) {
$map = $out = array();
foreach($a as $val) $map[$val] = 1;
foreach($b as $val) if(isset($map[$val])) $map[$val] = 0;
foreach($map as $val => $ok) if($ok) $out[] = $val;
return $out;
}
$a = array('A', 'B', 'C', 'D');
$b = array('X', 'C', 'A', 'Y');
print_r(my_array_diff($a, $b)); // B, D
benchmark
function your_array_diff($arraya, $arrayb)
{
foreach ($arraya as $keya => $valuea)
{
if (in_array($valuea, $arrayb))
{
unset($arraya[$keya]);
}
}
return $arraya;
}
$a = range(1, 10000);
$b = range(5000, 15000);
shuffle($a);
shuffle($b);
$ts = microtime(true);
my_array_diff($a, $b);
printf("ME =%.4f\n", microtime(true) - $ts);
$ts = microtime(true);
your_array_diff($a, $b);
printf("YOU=%.4f\n", microtime(true) - $ts);
result
ME =0.0137
YOU=3.6282
any questions? ;)
and, just for fun,
$ts = microtime(true);
array_diff($a, $b);
printf("PHP=%.4f\n", microtime(true) - $ts);
result
ME =0.0140
YOU=3.6706
PHP=19.5980
that's incredible!
The best solution to know how it works it to take a look at its source-code ;-)
(Well, that's one of the powers of open source -- and if you see some possible optimization, you can submit a patch ;-) )
For array_diff, it should be in ext/standard -- which means, for PHP 5.3, it should be there : branches/PHP_5_3/ext/standard
And, then, the array.c file looks like a plausible target ; the php_array_diff function, line 3381, seems to correspond to array_diff.
(Good luck going through the code : it's quite long...)
It seems you can speed it up a good deal more by using another array instead of unsetting. Though, this uses more memory, which might be an issue depeding on the use-case (I haven't tested actual differences in memory allocation).
<?php
function my_array_diff($a, $b) {
$map = $out = array();
foreach($a as $val) $map[$val] = 1;
foreach($b as $val) if(isset($map[$val])) $map[$val] = 0;
foreach($map as $val => $ok) if($ok) $out[] = $val;
return $out;
}
function leo_array_diff($a, $b) {
$map = $out = array();
foreach($a as $val) $map[$val] = 1;
foreach($b as $val) unset($map[$val]);
return array_keys($map);
}
function flip_array_diff_key($b, $a) {
$at = array_flip($a);
$bt = array_flip($b);
$d = array_diff_key($bt, $at);
return array_keys($d);
}
function flip_isset_diff($b, $a) {
$at = array_flip($a);
$d = array();
foreach ($b as $i)
if (!isset($at[$i]))
$d[] = $i;
return $d;
}
function large_array_diff($b, $a) {
$at = array();
foreach ($a as $i)
$at[$i] = 1;
$d = array();
foreach ($b as $i)
if (!isset($at[$i]))
$d[] = $i;
return $d;
}
$functions = array("flip_array_diff_key", "flip_isset_diff", "large_array_diff", "leo_array_diff", "my_array_diff", "array_diff");
#$functions = array_reverse($functions);
$l = range(1, 1000000);
$l2 = range(1, 1000000, 2);
foreach ($functions as $function) {
$ts = microtime(true);
for ($i = 0; $i < 10; $i++) {
$f = $function($l, $l2);
}
$te = microtime(true);
$timing[$function] = $te - $ts;
}
asort($timing);
print_r($timing);
My timings are (PHP 5.3.27-1~dotdeb.0):
[flip_isset_diff] => 3.7415699958801
[flip_array_diff_key] => 4.2989008426666
[large_array_diff] => 4.7882599830627
[flip_flip_isset_diff] => 5.0816700458527
[leo_array_diff] => 11.086831092834
[my_array_diff] => 14.563184976578
[array_diff] => 99.379411935806
The three new functions were found at http://shiplu.mokadd.im/topics/performance-optimization/
As this has been brought up (see #BurninLeo's answer), what about something like this?
function binary_array_diff($a, $b) {
$result = $a;
asort($a);
asort($b);
list($bKey, $bVal) = each($b);
foreach ( $a as $aKey => $aVal ) {
while ( $aVal > $bVal ) {
list($bKey, $bVal) = each($b);
}
if ( $aVal === $bVal ) {
unset($result[$aKey]);
}
}
return $result;
}
After performing some tests, results seem to be acceptable:
$a = range(1, 10000);
$b = range(5000, 15000);
shuffle($a);
shuffle($b);
$ts = microtime(true);
for ( $n = 0; $n < 10; ++$n ) {
array_diff($a, $b);
}
printf("PHP => %.4f\n", microtime(true) - $ts);
$ts = microtime(true);
for ( $n = 0; $n < 10; ++$n ) {
binary_array_diff($a, $b);
}
printf("binary => %.4f\n", microtime(true) - $ts);
$binaryResult = binary_array_diff($a, $b);
$phpResult = array_diff($a, $b);
if ( $binaryResult == $phpResult && array_keys($binaryResult) == array_keys($phpResult) ) {
echo "returned arrays are the same\n";
}
Output:
PHP => 1.3018
binary => 1.3601
returned arrays are the same
Of course, PHP code cannot perform as good as C code, therefore there's no wonder that PHP code is a bit slower.
From PHP: "Returns an array containing all the entries from array1 that are not present in any of the other arrays."
So, you just check array1 against all arrayN and any values in array1 that don't appear in any of those arrays will be returned in a new array.
You don't necessarily even need to loop through all of array1's values. Just for all the additional arrays, loop through their values and check if each value is in_array($array1, $value).

Categories