Comparing (empty) arrays in PHP - php

I want to write a test case to make sure a function call sets an array; however, I don't find a way to compare two arrays to make sure two empty arrays are not equal.
// code to be tested (simplified)
$foo = null;
function setFoo($input) {
global $foo;
$foo = array(); // BUG!!! The correct line would be: $foo = $input;
}
// test code
// given
$input = array();
// when
setFoo($input);
// then
if ($foo !== $input) {
// this block is not executed because "array() === array()" => true
throw new Exception('you have a bug');
}
So: What is the proper way to compare two PHP arrays and make sure, they are different instances (no matter if the content is the same)?

Memory locations refers to pointers. Pointers are not available in PHP. References are not pointers.
Anyway, if you want to check if $b is in fact a reference of $a, this is the closest you can get to an actual answer:
function is_ref_to(&$a, &$b) {
if (is_object($a) && is_object($b)) {
return ($a === $b);
}
$temp_a = $a;
$temp_b = $b;
$key = uniqid('is_ref_to', true);
$b = $key;
if ($a === $key) $return = true;
else $return = false;
$a = $temp_a;
$b = $temp_b;
return $return;
}
$a = array('foo');
$b = array('foo');
$c = &$a;
$d = $a;
var_dump(is_ref_to($a, $b)); // false
var_dump(is_ref_to($b, $c)); // false
var_dump(is_ref_to($a, $c)); // true
var_dump(is_ref_to($a, $d)); // false
var_dump($a); // is still array('foo')
I hope this solves your problem.

try this function . Arrays are compared like this with standard comparison operators
function standard_array_compare($op1, $op2)
{
if (count($op1) < count($op2)) {
return -1; // $op1 < $op2
} elseif (count($op1) > count($op2)) {
return 1; // $op1 > $op2
}
foreach ($op1 as $key => $val) {
if (!array_key_exists($key, $op2)) {
return null; // uncomparable
} elseif ($val < $op2[$key]) {
return -1;
} elseif ($val > $op2[$key]) {
return 1;
}
}
return 0; // $op1 == $op2
}

Related

How to check multidimensional arrays for the same key pairs ie.exactly same keys but values may differ?

I have made this solution, this is working fine for the normal arrays but failed for multidimensional arrays.
$a = ['a'=>11, 'b'=>2,'c'=>['r'=>5,'s'=>8]];
$b = ['a'=>1, 'b'=>2, 'c'=>['r'=>15,'s'=>18]];
function array_equal($a, $b) {
return (
is_array($a)
&& is_array($b)
&& count($a) == count($b)
&& array_diff_key($a, $b) === array_diff_key($b, $a)
);
}
$c = array_equal($a,$b);
echo $c;
For the Above set of arrays it is working fine.
But for the below arrays it returns 1 even if keys are different.
$a = ['a'=>11, 'b'=>2,'c'=>['r'=>5,'s'=>8]];
$b = ['a'=>1, 'b'=>2, 'c'=>['r1'=>15,'m'=>18]];
This would work if the array keys are in the same order:
https://3v4l.org/jDmON
<?php
function array_keys_recursive(array $array) : array
{
foreach ($array as $key => $value) {
if (is_array($value)) {
$index[$key] = array_keys_recursive($value);
} else {
$index []= $key;
}
}
return $index ?? [];
}
$a = ['a'=>11, 'b'=>2,'c'=>['r'=>5,'s'=>8]];
$b = ['a'=>1, 'b'=>2, 'c'=>['r'=>15,'s'=>18]];
var_dump(array_keys_recursive($a) === array_keys_recursive($b)); // true
$a = ['a'=>11, 'b'=>2,'c'=>['r'=>5,'s'=>8]];
$b = ['a'=>1, 'c'=>2, 'b'=>['r'=>15,'s'=>18]];
var_dump(array_keys_recursive($a) === array_keys_recursive($b)); // false
$a = ['a'=>11, 'b'=>2,'c'=>['r'=>5,'s'=>8]];
$b = ['a'=>1, 'b'=>2, 'c'=>['r1'=>15,'m'=>18]];
var_dump(array_keys_recursive($a) === array_keys_recursive($b)); // false
This should work -
function array_equal($a, $b) {
// count mismatch -> not equal
if (count($a) != count($b)) {
return false;
}
foreach ($a as $key => $val) {
// key not present -> not equal
if (empty($b[$key])) {
return false;
}
// check for inner arrays
if (is_array($val)) {
return array_equal($val, $b[$key]);
}
}
return true;
}
array_equal($a, $b); // true for first arrays
$a = ['a'=>11, 'b'=>2,'c'=>['r'=>5,'s'=>8]];
$b = ['a'=>1, 'b'=>2, 'c'=>['r1'=>15,'m'=>18]];
array_equal($a, $b); // false
I'm reading your question, as that you want to check for the same key structure, but don't care about the values.
I've cheated here by changing all the leaf values to null for both arrays, and then you can compare the leftovers.
<?php
$a = ['a'=>11, 'b'=>2, 'c'=> ['r'=>5, 's'=>8 ]];
$b = ['a'=>1, 'b'=>2, 'c'=> ['r'=>15,'s'=>18]];
function arrays_have_same_keys(array $a, array $b) {
array_walk_recursive($a, function(&$v) {
$v = null;
});
array_walk_recursive($b, function(&$v) {
$v = null;
});
return $a==$b;
}
var_dump(arrays_have_same_keys($a, $b));
Output:
bool(true)

Compare variable to multi variables in PHP

I have 5 variables $a $b $c $d $e . These variables has numerical values. Im trying to compare these variables where each variable will be compared to the rest and if the condition is true it echoes something. Here is my code
if ($a > ($b && $c && $d && $e)) {
$result = '<div>Im A</div>'
} else if ($b > ($a && $c && $d && $e)) {
$result = '<div>Im B</div>'
} else if ($c > ($a && $b && $d && $e)) {
$result = '<div>Im C</div>'
} else if ($d > ($a && $b && $c && $e)) {
$result = '<div>Im D</div>'
} else if ($e > ($a && $b && $c && $d)) {
$result = '<div>Im E</div>'
}
return $result;
The result stops at first condition even though it is false and it should pass it to other conditions.
Some different approach:
$a = 1;
$b = 3;
$c = 4;
$d = 5;
$e = 0;
// Make array [a=>1, b=>3...]
$arr = compact('a','b','c','d','e');
// Sort it in descending order with saving keys
arsort($arr);
// Get the 1st key
echo 'I\'m ' . strtoupper(key($arr)); // I'm D
First of all - you have parentheses around ($b && $c && $d && $e), this means that in $a > ($b && $c && $d && $e) the result of ($b && $c && $d && $e) will be counted first, and then will be compared to $a.
So, $a > ($b && $c && $d && $e) is not
$a is greater than $b and $a is greater than $c and etc.
it is
$a is greater than result of ($b and $c and $d and $e)
And result of $b and $c and $d and $e is either true or false.
So, in the end you compare $a > true or $a > false. According to value of $a you can get different results.
In a simple case, if you want to check if something is greater than anything else you need to write a condition like:
if ($a > $b && $a > $c && $a > $d && $a > $e) {
Other more tricky solutions you will find in other users' anwers.
You can iterate your five variables and keep track of the one with the highest value.
for ($i='a', $x = 0, $max = 'x'; $i <= 'e'; $i++) {
if ($$i > $$max) {
$max = $i;
}
}
$result = "<div>I'm " . strtoupper($max) . "</div>";
I think you should steer clear of if statements ;) Here's a solution without them that can be used with any number of variables. You could use the $key as an index into a list of functions and make the program extensible.
// Make the numbers into an array
$o = [$a, $b, $c, $d, $e];
// Find the highest value
$max = max($o);
// Look up the highest value to get the index
$key = array_search($max, $o);
// Now you know which one it is, you can do anything with it!
switch ($key) {
case 0: $result = '<div>Im A</div>'; break;
case 1: $result = '<div>Im B</div>'; break;
...
}
Try this:
$array = ['A'=>$a,'B'=>$b,' C'=>$c,'D'=>$d,'E'=> $e];
$maxs = array_keys($array, max($array));
foreach ($maxs as $maxi )
echo '<div>Im '.$maxi.'</div>."<br>"';
This way it covers cases were there are multiple max values.
Refer to this answer for more about how it works.

PHP use result of as variable

Is it possible to use the result of an if with an OR statement as a variable for a function?
As example:
$a = true;
$b = false;
if ($a || $b) {
$this->functionCall($a)
}
Other example:
$a = false;
$b = true;
if ($a || $b) {
$this->functionCall($b)
}
Third and final exmaple:
$a = true;
$b = true;
if ($a || $b) {
$this->functionCall($a, $b)
}
So I need to detect what variable is true and pass it as a paramater. Is this even possible?
Any helps is appreciated!
Many thanks in advance
I'd do the logic bit inside a two-parameter function if I were you, as such :
function myFunc($a = false, $b = false) {
if ($a == true)
echo 'a';
if ($b == true)
echo 'b';
}
myFunc(); // echoes nothing
$a = true;
$b = false;
myFunc($a, $b); // echoes 'a'
$a = false;
$b = true;
myFunc($a, $b); // echoes 'b'
$a = true;
$b = true;
myFunc($a, $b); // echoes 'ab'
PHP 5.6+ version, filter out the falsely values (you can pass a callback to array_filter for different checks) and use those with the splat operator.
$params = array_filter([$a, $b]);
$this->callFunction(...$params);
No need for any IF checks and confusing in IF assignments.
Explore Variadic functions and Argument unpacking.

Checking a for progression in a list of variables

Let's say I want to check for simple mathematical progression. I understand I can do it like this:
if ($a<$b and $b<$c and $c<$d and $d<$e and $e<$f) { echo OK; }
Is there a way to do it in a more convenient way? Like so
if ($a..$f isprog(<)) { echo OK; }
I don 't know if I get your problem right. But propably the solution for your progression could be the SplHeap object of the SPL delivered with php.
$stack = new SplMaxHeap();
$stack->insert(1);
$stack->insert(3);
$stack->insert(2);
$stack->insert(4);
$stack->insert(5);
foreach ($stack as $value) {
echo $value . "\n";
}
// output will be: 5, 4, 3, 2, 1
I havent heard of something like this, but how about using simple function:
function checkProgress($vars){ //to make it easie i assume that vars can be given in an array
$result = true;
for ($i=0; $i<= count($vars); $i++){
if ($i>0 && $vars[$i] > $vars[$i-1]) continue;
$result = false;
}
return $result;
}
Solved it quick and dirty:
function ispositiveprogression($vars) {
$num=count($vars)-1;
while ($num) {
$result = true;
if ($vars[$num] > $vars[$num-1]) {
$num--;
}
else { $result = false; break; }
}
return $result;
}
Create an array of values, iterate over them and maintaining a flag that checks if the current element value is greater than / less than that of the next value. Unlike some of the solutions in this thread, this doesn't loop through the whole array. It stops looping when it discovers the first value that's not a progression. This will be a lot more faster if the operation involves a lot of numbers.
function checkIfProg($arr, $compare) {
$flag = true;
for ($i = 0, $c = count($arr); $i < $c; $i++) {
if ($compare == '<') {
if (isset($arr[$i + 1]) && $arr[$i] > $arr[$i + 1]) {
$flag = false;
break;
}
} elseif ($compare == '>') {
if (isset($arr[$i + 1]) && $arr[$i] < $arr[$i + 1]) {
$flag = false;
break;
}
}
}
return $flag;
}
Usage:
$a = 2;
$b = 3;
$c = 4;
$d = 5;
$e = 9;
$f = 22;
$arr = array($a, $b, $c, $d, $e, $f);
var_dump(checkIfProg($arr, '<')); // => bool(true)
If you want the array to be created dynamically, you could use some variable variable magic to achieve this:
$arr = array();
foreach (range('a','f') as $v) {
$arr[] = $$v;
}
This will create an array containing all the values of variables from $a ... $f.

dynamic associative array?

I have array returned
$header_html = array(1=>array('width'=>40,
'sort_case'=>23,
'title'=>'AxA'),
2=>array('width'=>50,
'sort_case'=>7,
'title'=>'B2B'),
3=>array('width'=>100,
'sort_case'=>12,
'title'=>'C12')
);
I want to get new array that depend on $header_array=array('AxA','B2B','C12')
for examples:
if have $header_array=array('C12','B2B','AxA').
the new $header_html will be:
$header_html = array(
1=>array('width'=>100,
'sort_case'=>12,
'title'=>'C12'),
2=>array('width'=>50,
'sort_case'=>7,
'title'=>'B2B'),
3=>array('width'=>40,
'sort_case'=>23,
'title'=>'AxA')
);
and so on...
Anybody know how to do this?
You can sort the array with a custom comparison function using usort:
function cmp($a, $b) {
// Sort via $a['title'] and $b['title']
}
usort($header_html, 'cmp');
The trick is coming up with a comparison function that does what you want. To simply sort backwards by title, you could use:
function cmp($a, $b) {
if ($a['title'] == $b['title'])
return 0;
// usually return -1 if $a < $b, but we're sorting backwards
return ($a['title'] < $b['title'] ? 1 : -1;
}
In PHP 5.3, you can easily do this with a functor and usort.
class MyComparator {
protected $order = array();
public function __construct() {
$values = func_get_args();
$i = 0;
foreach($values as $v) {
$this->order[$v] = $i;
$i++;
}
}
public function __invoke($a, $b) {
$vala = isset($this->order[$a['title']]) ?
$this->order[$a['title']] : count($this->order);
$valb = isset($this->order[$b['title']]) ?
$this->order[$b['title']] : count($this->order);
if($vala == $valb) return 0;
return $vala < $valb ? -1 : 1;
}
}
You can use it like that:
$sorter = new MyComparator('CCC', 'AAA', 'BBB');
usort($header_html, $sorter);
You need a user-defined sort so you can access individual fields of the elements to sort:
function mysort($a, $b)
{
global $header_array;
$pos1 = array_search($a["title"], $header_array);
$pos2 = array_search($b["title"], $header_array);
if ($pos1 == $pos2) { return 0; }
return $pos1 < $pos2 ? -1 : 1;
}
$header_array = array("CCC", "BBB", "AAA");
usort($header_html, "mysort");
print_r($header_array);
note: usort() returns true on success or false on failure; it does not return the resorted array.
It sounds like you want a function to return the array elements in the order you specify in $header_array. If so, here's a stab:
function header_resort($header_array, $header_html) {
foreach($header_array as $i => $val) {
foreach($header_html as $obj) {
if( $obj->title == $val )
$header_html_new[$i] = $obj;
}
}
return $header_html_new;
}

Categories