Does PHPUnit have some inbuilt recursive array comparison function? - php

Some of the testing I will need to do will require comparing a known array with the result I am getting from the functions I will be running.
For comparing arrays recursively:
Does PHPUnit have an inbuilt function?
Does someone here have some code they have constructed to share?
Will this be something I will have to construct on my own?

Yes it does. assertEquals() and assertNotEquals() documentation.
Specifically:
assertEquals()
assertEquals(mixed $expected, mixed $actual[, string $message = ''])
Reports an error identified by $message if the two variables $expected and $actual are not equal.
assertNotEquals() is the inverse of this assertion and takes the same arguments.
Test Code:
public function testArraysEqual() {
$arr1 = array( 'hello' => 'a', 'goodbye' => 'b');
$arr2 = array( 'hello' => 'a', 'goodbye' => 'b');
$this->assertEquals($arr1, $arr2);
}
public function testArraysNotEqual() {
$arr1 = array( 'hello' => 'a', 'goodbye' => 'b');
$arr2 = array( 'hello' => 'b', 'goodbye' => 'a');
$this->assertNotEquals($arr1, $arr2);
}
[EDIT]
Here is the code for out of order aLists:
public function testArraysEqualReverse() {
$arr1 = array( 'hello' => 'a', 'goodbye' => 'b');
$arr2 = array( 'goodbye' => 'b', 'hello' => 'a');
$this->assertEquals($arr1, $arr2);
}
This test fails:
public function testArraysOutOfOrderEqual() {
$arr1 = array( 'a', 'b');
$arr2 = array( 'b', 'a');
$this->assertEquals($arr1, $arr2);
}
With message:
Failed asserting that
Array
(
[0] => b
[1] => a
)
is equal to
Array
(
[0] => a
[1] => b
)

#wilmoore
$array1 = array('hi','hi2');
$array2 = array('hi2','hi');
$this->assertEquals(array_values($array1), array_values($array2));
Will fail.
#Ben Dauphinee
It might be worth looking at assertContains(mixed $needle, array $haystack) but you would have to loop through both arrays and compare each element with the other array to ensure it contained all the required elements and no others, this however wouldn't account for an array containing two identical elements
$array1 = array('hi','hi2','hi');
$array2 = array('hi2','hi');
would pass in this case
It also doesn't account for any further recursion which would probably be quite complicated to deal with.
Depending on the complexity might just be easier in the long run to implement your own assert function.

The assertEqual method head looks like this:
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE)
If the canonicalize parameter is set to true the arrays will be run through sort() first, this is usable if the keys are arbitrary and only the values matters.
However after looking over the array comparator code, the assertEqual actually does not care about the order of an associated array :) It will simply look for the expected key in the result array, and then compare the values of those keys.

I had this problem with some generated arrays with keys - I ended up passing both the expected array and the array being tested through ksort before calling assertEquals. This doesn't work on recursive arrays, though.
$expectedArray = array('foo' => 1, 'bar' => 0);
$array = array('bar' => 0, 'foo' => 1);
ksort($expectedArray);
ksort($array);
var_dump($expectedArray); // outputs array('bar' => 0, 'foo' => 1);
var_dump($array); // outputs array('bar' => 0, 'foo' => 1);
$this->assertEquals($expectedArray, $array); // passes

Related

how to fix array_udiff while having strings inside array [duplicate]

I have an array containing rows of associative data.
$array1 = array(
array('ITEM' => 1),
array('ITEM' => 2),
array('ITEM' => 3),
);
I have a second array, also containing rows of associative data, that I would like to filter using the first array.
$array2 = array(
array('ITEM' => 2),
array('ITEM' => 3),
array('ITEM' => 1),
array('ITEM' => 4),
);
This feels like a job for array_diff(), but how can I compare the rows exclusively on the deeper ITEM values?
How can I filter the second array and get the following result?
array(3 => array('ITEM' => 4))
You can define a custom comparison function using array_udiff().
function udiffCompare($a, $b)
{
return $a['ITEM'] - $b['ITEM'];
}
$arrdiff = array_udiff($arr2, $arr1, 'udiffCompare');
print_r($arrdiff);
Output:
Array
(
[3] => Array
(
[ITEM] => 4
)
)
This uses and preserves the arrays' existing structure, which I assume you want.
I would probably iterate through the original arrays and make them 1-dimensional... something like
foreach($array1 as $aV){
$aTmp1[] = $aV['ITEM'];
}
foreach($array2 as $aV){
$aTmp2[] = $aV['ITEM'];
}
$new_array = array_diff($aTmp1,$aTmp2);
Another fun approach with a json_encode trick (can be usefull if you need to "raw" compare some complex values in the first level array) :
// Compare all values by a json_encode
$diff = array_diff(array_map('json_encode', $array1), array_map('json_encode', $array2));
// Json decode the result
$diff = array_map('json_decode', $diff);
A couple of solutions using array_filter that are less performant than the array_udiff solution for large arrays, but which are a little more straightforward and more flexible:
$array1 = [
['ITEM' => 1],
['ITEM' => 2],
['ITEM' => 3],
];
$array2 = [
['ITEM' => 2],
['ITEM' => 3],
['ITEM' => 1],
['ITEM' => 4],
];
$arrayDiff = array_filter($array2, function ($element) use ($array1) {
return !in_array($element, $array1);
});
// OR
$arrayDiff = array_filter($array2, function ($array2Element) use ($array1) {
foreach ($array1 as $array1Element) {
if ($array1Element['ITEM'] == $array2Element['ITEM']) {
return false;
}
}
return true;
});
As always with array_filter, note that array_filter preserves the keys of the original array, so if you want $arrayDiff to be zero-indexed, do $arrayDiff = array_values($arrayDiff); after the array_filter call.
you can use below code to get difference
$a1 = Array(
[0] => Array(
[ITEM] => 1
)
[1] => Array(
[ITEM] => 2
)
[2] => Array(
[ITEM] => 3
)
);
$a2 = Array(
[0] => Array(
[ITEM] => 2
)
[1] => Array(
[ITEM] => 3
)
[2] => Array(
[ITEM] => 1
)
[3] => Array(
[ITEM] => 4
));
array_diff(array_column($a1, 'ITEM'), array_column($a2, 'ITEM'));
Having the same problem but my multidimensional array has various keys unlike your "ITEM" consistently in every array.
Solved it with: $result = array_diff_assoc($array2, $array1);
Reference: PHP: array_diff_assoc
Another solution
if( json_encode($array1) == json_encode($array2) ){
...
}
Trust that the maintainers of PHP have optimized array_udiff() to outperform all other techniques which could do the same.
With respect to your scenario, you are seeking a filtering array_diff() that evaluates data within the first level's "value" (the row of data). Within the custom function, the specific column must be isolated for comparison. For a list of all native array_diff() function variations, see this answer.
To use the first array to filter the second array (and output the retained data from the second array), you must write $array2 as the first parameter and $array1 as the second parameter.
array_diff() and array_intersect() functions that leverage (contain u in their function name) expect an integer as their return value. That value is used to preliminary sort the data before actually performing the evaluations -- this is a performance optimization. There may be scenarios where if you only return 0 or 1 (not a three-way comparison), then the results may be unexpected. To ensure a stable result, always provide a comparison function that can return a negative, a positive, and a zero integer.
When comparing integer values, subtraction ($a - $b) will give reliable return values. For greater utility when comparing float values or non-numeric data, you can use the spaceship operator when your PHP version makes it available.
Codes: (Demo)
PHP7.4+ (arrow functions)
var_export(
array_udiff($array2, $array1, fn($a, $b) => $a['ITEM'] <=> $b['ITEM'])
);
PHP7+ (spaceship operator)
var_export(
array_udiff(
$array2,
$array1,
function($a, $b) {
return $a['ITEM'] <=> $b['ITEM'];
}
)
);
PHP5.3+ (anonymous functions)
var_export(
array_udiff(
$array2,
$array1,
function($a, $b) {
return $a['ITEM'] === $b['ITEM']
? 0
: ($a['ITEM'] > $b['ITEM'] ? 1 : -1);
}
)
);
Output for all version above:
array (
3 =>
array (
'ITEM' => 4,
),
)
When working with object arrays, the technique is the same; only the syntax to access a property is different from accessing an array element ($a['ITEM'] would be $a->ITEM).
For scenarios where the element being isolated from one array does not exist in the other array, you will need to coalesce both $a and $b data to the opposite fallback column because the data from the first array and the second arrays will be represented in both arguments of the callback.
Code: (Demo)
$array1 = array(
array('ITEM' => 1),
array('ITEM' => 2),
array('ITEM' => 3),
);
$array2 = array(
array('ITEMID' => 2),
array('ITEMID' => 3),
array('ITEMID' => 1),
array('ITEMID' => 4),
);
// PHP7.4+ (arrow functions)
var_export(
array_udiff(
$array2,
$array1,
fn($a, $b) => ($a['ITEM'] ?? $a['ITEMID']) <=> ($b['ITEM'] ?? $b['ITEMID'])
)
);
Compares array1 against one or more other arrays and returns the values in array1 that are not present in any of the other arrays.
//Enter your code here, enjoy!
$array1 = array("a" => "green", "red", "blue");
$array2 = array("b" => "green", "yellow", "red");
$result = array_diff($array1, $array2);
print_r($result);

create array of POSTs from string array [duplicate]

I am searching for a built in php function that takes array of keys as input and returns me corresponding values.
for e.g. I have a following array
$arr = array("key1"=>100, "key2"=>200, "key3"=>300, 'key4'=>400);
and I need values for the keys key2 and key4 so I have another array("key2", "key4")
I need a function that takes this array and first array as inputs and provide me values in response. So response will be array(200, 400)
I think you are searching for array_intersect_key. Example:
array_intersect_key(array('a' => 1, 'b' => 3, 'c' => 5),
array_flip(array('a', 'c')));
Would return:
array('a' => 1, 'c' => 5);
You may use array('a' => '', 'c' => '') instead of array_flip(...) if you want to have a little simpler code.
Note the array keys are preserved. You should use array_values afterwards if you need a sequential array.
An alternative answer:
$keys = array("key2", "key4");
return array_map(function($x) use ($arr) { return $arr[$x]; }, $keys);
foreach($input_arr as $key) {
$output_arr[] = $mapping[$key];
}
This will result in $output_arr having the values corresponding to a list of keys in $input_arr, based on the key->value mapping in $mapping. If you want, you could wrap it in a function:
function get_values_for_keys($mapping, $keys) {
foreach($keys as $key) {
$output_arr[] = $mapping[$key];
}
return $output_arr;
}
Then you would just call it like so:
$a = array('a' => 1, 'b' => 2, 'c' => 3);
$values = get_values_for_keys($a, array('a', 'c'));
// $values is now array(1, 3)

multidimensional array to one dimensional array recursively

I have this multidimensional array
$liste = [[1,2,3],5,[['x','y','z'],true]];
and I want to change it to one dimensionel array
$liste = [1,2,3,5,'x','y','z',true];
so i always have a problem that give me the same shape
function to_array($list){
$out=[];
if(!is_array($list)){
return $list;
}else{
foreach($list as $line){
$out[]= to_array($line);
}
}
return $out;
}
where is the problem in this recursive function !!!
The issue with your code is that you are pushing the result of to_array into $out, when what you want to do is use array_merge. Now since that requires both parameters to be arrays, when $list is not an array, you need to return an array containing the individual value. So change these lines:
return $list;
$out[]= to_array($line);
To:
return array($list);
$out = array_merge(to_array($line));
i.e.
function to_array($list){
$out=[];
if(!is_array($list)){
return array($list);
}else{
foreach($list as $line){
$out = array_merge($out, to_array($line));
}
}
return $out;
}
And you will get the result that you want:
var_export(to_array($liste));
Output:
array (
0 => 1,
1 => 2,
2 => 3,
3 => 5,
4 => 'x',
5 => 'y',
6 => 'z',
7 => true,
)
array_walk_recursive() delivers the desired result from an array of indeterminate depth in a one-liner because it only visits the "leaf-nodes" -- effectively, you don't need to bother checking if an element is or is not an array.
array_walk_recursive() doesn't return an array, it returns true|false based on whether or not there was a failure.
&$flat is a variable which is "passed by reference". This means that $flat can act as a vehicle to transport the data from inside the function scope to outside the function scope. As the elements are traversed, each new value is pushed into $flat using square bracket syntax.
This is exactly what this function does best -- use it.
Code: (Demo)
$liste = [[1, 2, 3], 5, [['x', 'y', 'z'], true]];
array_walk_recursive($liste, function($v) use (&$flat){ $flat[] = $v; });
var_export($flat);
Output:
array (
0 => 1,
1 => 2,
2 => 3,
3 => 5,
4 => 'x',
5 => 'y',
6 => 'z',
7 => true,
)

How can I efficiently split an array into its associative key arrays?

How can I split a single array into it's sub-keys?
$arr = array(
0 => array(
'foo' => '1',
'bar' => 'A'
),
1 => array(
'foo' => '2',
'bar' => 'B'
),
2 => array(
'foo' => '3',
'bar' => 'C'
)
);
What is the most efficient way to return an array of foo and bar separately?
I need to get here:
$foo = array('1','2','3');
$bar = array('A','B','C');
I'm hoping there's a clever way to do this using array_map or something similar. Any ideas?
Or do I have to loop through and build each array that way? Something like:
foreach ($arr as $v) {
$foo[] = $v['foo'];
$bar[] = $v['bar'];
}
In a lucky coincidence, I needed to do almost the exact same thing earlier today. You can use array_map() in combination with array_shift():
$foo = array_map('array_shift', &$arr);
$bar = array_map('array_shift', &$arr);
Note that $arr is passed by reference! If you don't do that, then each time it would return the contents of $arr[<index>]['foo']. However, again because of the reference - you won't be able to reuse $arr, so if you need to do that - copy it first.
The downside is that your array keys need to be ordered in the same way as in your example, because array_shift() doesn't actually know what the key is. It will NOT work on the following array:
$arr = array(
0 => array(
'foo' => '1',
'bar' => 'A'
),
1 => array(
'bar' => 'B',
'foo' => '2'
),
2 => array(
'foo' => '3',
'bar' => 'C'
)
);
Update:
After reading the comments, it became evident that my solution triggers E_DEPRECATED warnings for call-time-pass-by-reference. Here's the suggested (and accepted as an answer) alternative by #Baba, which takes advantage of the two needed keys being the first and last elements of the second-dimension arrays:
$foo = array_map('array_shift', $arr);
$bar = array_map('array_pop', $arr);
$n = array();
foreach($arr as $key=>$val) {
foreach($val as $k=>$v) {
$n[$k][] = $v;
}
}
array_merge_recursive will combine scalar values with the same key into an array. e.g.:
array_merge_recursive(array('a',1), array('b',2)) === array(array('a','b'),array(1,2));
You can use this property to simply apply array_merge_recursive over each array in your array as a separate argument:
call_user_func_array('array_merge_recursive', $arr);
You will get this result:
array (
'foo' =>
array (
0 => '1',
1 => '2',
2 => '3',
),
'bar' =>
array (
0 => 'A',
1 => 'B',
2 => 'C',
),
)
It won't even be confused by keys in different order.
However, every merged value must be scalar! Arrays will be merged instead of added as a sub-array:
array_merge_recursive(array(1), array(array(2)) === array(array(1,2))
It does not produce array(array(1, array(2)))!

Accessing array elements using the associative index and numbered index

Is it possible to define an array where I can access the elements via their string and numeric index?
array_values() will return all values in an array with their indices replaced with numeric ones.
http://php.net/array-values
$x = array(
'a' => 'x',
'b' => 'y'
);
$x2 = array_values($x);
echo $x['a']; // 'x'
echo $x2[0]; // 'x'
The alternative is to build a set of by-reference indices.
function buildReferences(& $array) {
$references = array();
foreach ($array as $key => $value) {
$references[] =& $array[$key];
}
$array = array_merge($references, $array);
}
$array = array(
'x' => 'y',
'z' => 'a'
);
buildReferences($array);
Note that this should only be done if you're not planning on adding or removing indices. You can edit them though.
You can do this.
$arr = array(1 => 'Numerical', 'two' => 'string');
echo $arr[1]; //Numerical
echo $arr['two']; //String
martswite's answer is correct, although if you've already got an associative array it may not solve your problem. The following is an ugly hack to work around this - and should be avoided at all cost:
$a = array(
'first' => 1,
'second' => 2,
'third' => 3
);
$b=array_values($a);
print $b[2];
PHP allows a mixture of string and numeric-indexed elements.
$array = array(0=>'hello','abc'=>'world');
echo $array[0]; // returns 'hello'
echo $array['0']; // returns 'hello'
echo $array['abc']; // returns 'world';
echo $array[1]; // triggers a PHP notice: undefined offset
A closer look at the last item $array[1] reveals that it is not equivalent to the 2nd element of the array.

Categories