PHP merge array on nulls - php

I have 2 * array and I want to merge them, but each of theme have some NULL rows.
$a = array(
'a' => NULL,
'b' => 1,
'c' => 1
);
$b = array(
'a' => 1,
'b' => NULL,
'c' => 1
);
So, code:
$c = array_merge($a,$b);
Will give $c:
array {
'a'=> 1
'b'=> NULL
'c'=>1
}
Is there build in or simple way to do margin ($a,$b) like following, but now $a is overwritten for every same index from $b. I want $b to be overwritten by $a index if $b index is null - in example $b['b'] should be overwritten from $a
All NULL rows should be filled if possible.

I think you can use array_filter function to remove null values in both array and then merge them
$a = array(
'a' => NULL,
'b' => 1,
'c' => 1
);
$b = array(
'a' => 1,
'b' => NULL,
'c' => 1
);
$b = array_filter($b);
$a = array_filter($a);
$c = array_merge($a, $b);
var_dump($c);
This will output
array(3) {
["b"]=> int(1)
["c"]=> int(1)
["a"]=> int(1)
}
LIVE SAMPLE
As side note i would add that using array_filter without second parameter will end up in deleting all NULL values as well as EMPTY array etc. If you want to only remove NULL values so you will need to use array_filter($yourarray, 'strlen');
EDITED
If you want to preserve NULL if both arrays have it with the same key and supposing that both arrays have same number of keys/values, then you will need to loop inside your array and build a new array preserving NULL where you need
$a = array(
'a' => NULL,
'b' => 1,
'c' => 1,
'd' => NULL
);
$b = array(
'a' => 1,
'b' => NULL,
'c' => 1,
'd' => NULL,
);
$c = array();
foreach($a as $key => $val)
{
if($key == NULL && $b[$key] == NULL)
{
$c[$key] = $val;
} else if($key != NULL && $b[$key] == NULL) {
$c[$key]= $val;
} else if($key != NULL && $b[$key] != NULL) {
$c[$key]= $b[$key];
} else {
$c[$key]= $b[$key];
}
}
var_dump($c);
This will output
array (size=4)
'a' => int 1
'b' => int 1
'c' => int 1
'd' => NULL
LIVE SAMPLE

I would do it with a simple one liner like this:
$c = array_filter($b) + array_filter($a) + array_fill_keys(array_keys($b),null);
This would preserve all keys and values of $b except where falsey in which case they will be either replaced with values from $a or as a last resort replaced with null.
When merging arrays with the + operator the order in which they appear from left to right equates to the precedence. So the leftmost position will hold it's value unless the key does not exist.
You can also reverse the order and use array_merge(), but for me it's harder on the eyes:
$c = array_merge(array_fill_keys(array_keys($b),null) + array_filter($a) + array_filter($b));

None of these answers address merging two arrays that may have different keys, which is what lead me to this SO post. It happens to actually be pretty straight forward fortunately:
function arrayMergeIfNotNull($arr1, $arr2) {
foreach($arr2 as $key => $val) {
$is_set_and_not_null = isset($arr1[$key]);
if ( $val == NULL && $is_set_and_not_null ) {
$arr2[$key] = $arr1[$key];
}
}
return array_merge($arr1, $arr2);
}
Now, merging these two arrays:
$a = array('a' => NULL, 'b' => 1, 'c' => 1, 'd' => NULL, 'z' => 'zebra');
$b = array('a' => 1, 'b' => NULL, 'c' => 1, 'd' => NULL, 'f' => 'frank');
with:
var_dump(arrayMergeIfNotNull($a, $b));
will produce:
array (size=6)
'a' => int 1
'b' => int 1
'c' => int 1
'd' => NULL
'z' => 'zebra'
'f' => 'frank'
Note this answer also solves the problem of two arrays with the same keys.

then you have to pass $b as the first parameter
$c = array_merge($b,$a);
you can use this function
function mergeArray($array1, $array2)
{
$result = array();
foreach ($array1 as $key=>$value)
{
$result[$key] = $value;
}
foreach ($array2 as $key=>$value)
{
if (!isset($result[$key] || $result[$key] == null)
{
$result[$key] = $value;
}
}
return $result;
}

If you need to preserve keys that have a value of NULL in both arrays, you can use a custom function that ignores those entries from the second array if (and only if) there is a corresponding non-NULL entry in the first array. It would look like this:
function arrayMergeIgnoringNull($arr1, $arr2) {
$new2 = array();
forEach ($arr2 as $key => $value) {
if (($value !== NULL) || !isSet($arr1[$key])) {
$new2[$key] = $value;
}
}
return array_merge($arr1, $new2);
}
$c = arrayMergeIgnoringNull($a, $b);
See, also, this short demo.

Related

Filter associative array to keep elements when their value is greater than their neighboring elements' values

I have an associative array from which I need a new array to be created. I am targeting the peak values for the new array. For a value to be selected to the new array, it needs to be higher than both the previous and the next value in the array.
I have searched the net and found a function that could handle the situation for indexed arrays - but not for associative arrays. Here is what I would like:
$array = array('a' => 0, 'b' => 2, 'c' => 1, 'd' => 2, 'e' => 3);
$result = array('b' => 2, 'e' => 3);
The function usable for indexed arrays look like this:
$a = array(0, 2, 1, 2, 3);
$b = array_filter($a, function($v, $k) use($a) {
return $k > 0 && $v > $a[$k-1] && $k + 1 < count($a) && $v > $a[$k+1];
}, ARRAY_FILTER_USE_BOTH );
This function doesn't include the last value either.
You should change the condition if you really want to get border items. But an approach could be to use the array of keys to get prev and next items
$array = array('a' => 0, 'b' => 2, 'c' => 1, 'd' => 2, 'e' => 3);
$keys = array_keys($array);
$b = array_filter($array, function($v, $k) use($array, $keys) {
$k = array_search($k, $keys);
return $k > 0 && $v > $array[$keys[$k-1]] && $k + 1 < count($keys) && $v > $array[$keys[$k+1]];
}, ARRAY_FILTER_USE_BOTH );
print_r($b);
This can be done with zero iterated function calls.
Before looping declare a lookup array by assigning indices to the input array's keys -- array_keys().
Then in the loop use conditional fallback values and null coalescing when an attempt to access a non-existent element occurs.
($i ? $array[$keys[$i - 1]] : $array[$key] - 1) means: if $i is not zero, then access the value from the input array by its key which is one position before the current key. Otherwise, $i equals zero which means there is no earlier value so fallback to the current value minus one to guarantee it will be less in the comparison.
($array[$keys[$i + 1] ?? -1] ?? $array[$key] - 1) means: try to access the next key in the lookup array, if it does not exist, use negative one which is guaranteed not to exist. By effect, when $i is that last index in the loop, there will be no next key, so fallback to the current value minus one again.
This should always outperform any script that makes iterated function calls.
Code: (Demo)
$array = ['a' => 0, 'b' => 2, 'c' => 1, 'd' => 2, 'e' => 3];
$keys = array_keys($array);
foreach ($keys as $i => $key) {
if ($array[$key] > ($i ? $array[$keys[$i - 1]] : $array[$key] - 1)
&& $array[$key] > ($array[$keys[$i + 1] ?? -1] ?? $array[$key] - 1)
) {
$result[$key] = $array[$key];
}
}
var_export($result);
Output:
array (
'b' => 2,
'e' => 3,
)
Alternatively, if you want to mutate the input array, you can call unset() in the loop and simply invert the conditional logic.
I worked #splash58. Thank you. I have none the less tried something myself which also worked - but I don't know which solution is most effective.
$result = array();
$value1 = NULL;
foreach (array_reverse($array) as $key => $value) {
if ($value > $value1) {
$result[$key] = $value;
}
$value1 = $value;
}
and is '$value1 = NULL' this necessary?

Merge 2 arrays after a specific key in PHP

I have 2 arrays:
$array["a"];
$array["b"];
$array["c"];
$array["d"];
$otherarray["otherkey"];
I want to merge it and that $otherarray["otherkey"] is right after $array["b"]
I am using array_merge($array,$otherarray) but this creates this array:
$newarray["a"];
$newarray["b"];
$newarray["c"];
$newarray["d"];
$newarray["otherkey"];
Is there a way to insert it right after ["b"] so it becomes:
$newarray["a"];
$newarray["b"];
$newarray["otherkey"];
$newarray["c"];
$newarray["d"];
Maybe you can try somehting like this :
// Your first array
$array["a"] = "a";
$array["b"] = "b";
$array["c"] = "c";
$array["d"] = "d";
// Your second array
$otherarray["otherkey"] = "otherkey";
// I create a function with 4 param :
// $first_array : the first array you want to merge
// $second_array : the second array you want to merge
// $merge_after_key : the key of the first array you will use to know when your merge the second array
// $otherkey : the key of the second array you will merge
function mergeArrayByKey($first_array, $second_array, $merge_after_key, $otherkey) {
// I create a new array
$new_array = array();
// Now I will loop through the first array and create the new array for each key
foreach ($first_array as $key => $value) {
$new_array[$key] = $value;
// When key of first array meet the merge after key : I know I need to add the second array to my result array
if ($key === $merge_after_key) {
$new_array[$otherkey] = $second_array[$otherkey];
}
}
// Then I return the new array
return $new_array;
}
// Now you can use it and change the value if you need.
// For your example I use your two array + the key 'b' and the 'otherkey'
$result = mergeArrayByKey($array, $otherarray, 'b', 'otherkey');
var_dump($result);
Output is :
array (size=5)
'a' => string 'a' (length=1)
'b' => string 'b' (length=1)
'otherkey' => string 'otherkey' (length=8)
'c' => string 'c' (length=1)
'd' => string 'd' (length=1)
This is another option:
<?php
$first = ["a" => 1, "b" => 2, "c" => 3, "d" => 4];
$second = ["inserted" => "here", "and" => "also here"];
function mergeAtKey($first, $second, $key) {
$bothParametersAreArrays = is_array($first) && is_array($second);
$requestedKeyExistsInFirstArray = array_key_exists($key, $first);
if (!($bothParametersAreArrays && $requestedKeyExistsInFirstArray)) {
throw new Exception;
}
$keyPositionInFirstArray = array_search($key, array_keys($first)) + 1;
$firstSlice = array_slice($first, 0, $keyPositionInFirstArray, true);
$secondSlice = array_slice($first, $keyPositionInFirstArray, count($first) - 1, true);
return $firstSlice + $second + $secondSlice;
}
print_r(mergeAtKey($first, $second, "b"));

Get value from array by key or falsey value if key doesn't exist / isn't truthy

I have the following array:
$a = array('a' => 1,'b' => 2,'c' => null);
I want to find a way of accessing the value of an element for a given key, and if the key doesn't exist, a falsey value.
With the example above, I'd like $a['d'] to give me a falsey value. (similar to JavaScript: ({}).b // -> undefined).
How can I do that?
Edit: In my specific case, I do not care if e.g. $a['c'] => false.
In PHP 7.0 and upwards you can use the null coalescing operator:
$d = $a['d'] ?? false;
In PHP 5.3 and upwards you can use a ternary statement:
$d = isset($a['d']) ? $a['d'] : false;
Tested below in PHP7.0.20
PHP Script
$a = array('a' => 1,'b' => 2,'c' => 3);
$b1 = $a['b'] ?? false;
$b2 = isset($a['b']) ? $a['b'] : false;
$b3 = $a['b'] ?: false;
$d1 = $a['d'] ?? false;
$d2 = isset($a['d']) ? $a['d'] : false;
// Undefined Error
// $d3 = $a['d'] ?: false;
var_dump([
'b1' => $b1,
'b2' => $b2,
'b3' => $b3,
'd1' => $d1,
'd2' => $d2,
// 'd3' => $d3
]);
Console output
| => php test.php
array(5) {
["b1"]=>int(2)
["b2"]=>int(2)
["b3"]=>int(2)
["d1"]=>bool(false)
["d2"]=>bool(false)
}
For reference, see:
http://php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary
http://php.net/manual/de/migration70.new-features.php#migration70.new-features.null-coalesce-op)
maybe you should try:
echo empty($a['d'])?false:$a['d'];
There is a function for this...
THE ONLY CORRECT WAY
$my_array = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => null);
if(array_key_exists('d', $my_array)){
// Do something
}
else{
// Do something else
}
Also, pay extra attention to 'd' => null because databases will gladly return null values
// The almost right way
$my_array = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => null);
$result = array_key_exists( 'd', $my_array ) ? $my_array['d'] : false;
var_dump( $result );
// The wrong way
$result = isset($my_array['d']) ? $my_array['d'] : false;
var_dump( $result );
I posted "The almost right way" because the value can literally be a false, see below:
$my_array = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => false);
// $result it going to be false but there is no way to tell if this is due to the key being missing or if the key's value is literally false
$result = array_key_exists( 'd', $my_array ) ? $my_array['d'] : false;
If the above occurs then all logic goes out the window.

How to check if a single multi dimensional array contails same array as value in php? [duplicate]

I'd like to check if two arrays are equal. I mean: same size, same index, same values. How can I do that?
Using !== as suggested by a user, I expect that the following would print enter if at least one element in the array(s) are different, but in fact it does not.
if (($_POST['atlOriginal'] !== $oldAtlPosition)
or ($_POST['atl'] !== $aext)
or ($_POST['sidesOriginal'] !== $oldSidePosition)
or ($_POST['sidesOriginal'] !== $sideext)) {
echo "enter";
}
$arraysAreEqual = ($a == $b); // TRUE if $a and $b have the same key/value pairs.
$arraysAreEqual = ($a === $b); // TRUE if $a and $b have the same key/value pairs in the same order and of the same types.
See Array Operators.
EDIT
The inequality operator is != while the non-identity operator is !== to match the equality
operator == and the identity operator ===.
According to this page.
NOTE: The accepted answer works for associative arrays, but it will not work as expected with indexed arrays (explained below). If you want to compare either of them, then use this solution. Also, this function may not works with multidimensional arrays (due to the nature of array_diff function).
Testing two indexed arrays, which elements are in different order, using $a == $b or $a === $b fails, for example:
<?php
(array("x","y") == array("y","x")) === false;
?>
That is because the above means:
array(0 => "x", 1 => "y") vs. array(0 => "y", 1 => "x").
To solve that issue, use:
<?php
function array_equal($a, $b) {
return (
is_array($a)
&& is_array($b)
&& count($a) == count($b)
&& array_diff($a, $b) === array_diff($b, $a)
);
}
?>
Comparing array sizes was added (suggested by super_ton) as it may improve speed.
Try serialize. This will check nested subarrays as well.
$foo =serialize($array_foo);
$bar =serialize($array_bar);
if ($foo == $bar) echo "Foo and bar are equal";
Short solution that works even with arrays which keys are given in different order:
public static function arrays_are_equal($array1, $array2)
{
array_multisort($array1);
array_multisort($array2);
return ( serialize($array1) === serialize($array2) );
}
function compareIsEqualArray(array $array1,array $array2):bool
{
return (array_diff($array1,$array2)==[] && array_diff($array2,$array1)==[]);
}
Compare them as other values:
if($array_a == $array_b) {
//they are the same
}
You can read about all array operators here:
http://php.net/manual/en/language.operators.array.php
Note for example that === also checks that the types and order of the elements in the arrays are the same.
if (array_diff($a,$b) == array_diff($b,$a)) {
// Equals
}
if (array_diff($a,$b) != array_diff($b,$a)) {
// Not Equals
}
From my pov it's better to use array_diff than array_intersect because with checks of this nature the differences returned commonly are less than the similarities, this way the bool conversion is less memory hungry.
Edit Note that this solution is for plain arrays and complements the == and === one posted above that is only valid for dictionaries.
Another method for checking equality regardless of value order works by using http://php.net/manual/en/function.array-intersect.php, like so:
$array1 = array(2,5,3);
$array2 = array(5,2,3);
if($array1 === array_intersect($array1, $array2) && $array2 === array_intersect($array2, $array1)) {
echo 'Equal';
} else {
echo 'Not equal';
}
Here's a version that works also with multidimensional arrays using http://php.net/manual/en/function.array-uintersect.php:
$array1 = array(
array(5, 2),
array(3, 6),
array(2, 9, 4)
);
$array2 = array(
array(3, 6),
array(2, 9, 4),
array(5, 2)
);
if($array1 === array_uintersect($array1, $array2, 'compare') && $array2 === array_uintersect($array2, $array1, 'compare')) {
echo 'Equal';
} else {
echo 'Not equal';
}
function compare($v1, $v2) {
if ($v1===$v2) {
return 0;
}
if ($v1 > $v2) return 1;
return -1;
}
One way: (implementing 'considered equal' for https://www.rfc-editor.org/rfc/rfc6902#section-4.6)
This way allows associative arrays whose members are ordered differently - e.g. they'd be considered equal in every language but php :)
// recursive ksort
function rksort($a) {
if (!is_array($a)) {
return $a;
}
foreach (array_keys($a) as $key) {
$a[$key] = ksort($a[$key]);
}
// SORT_STRING seems required, as otherwise
// numeric indices (e.g. "0") aren't sorted.
ksort($a, SORT_STRING);
return $a;
}
// Per https://www.rfc-editor.org/rfc/rfc6902#section-4.6
function considered_equal($a1, $a2) {
return json_encode(rksort($a1)) === json_encode(rksort($a2));
}
Syntax problem on your arrays
$array1 = array(
'a' => 'value1',
'b' => 'value2',
'c' => 'value3',
);
$array2 = array(
'a' => 'value1',
'b' => 'value2',
'c' => 'value3',
);
$diff = array_diff($array1, $array2);
var_dump($diff);
Here is the example how to compare to arrays and get what is different between them.
$array1 = ['1' => 'XXX', 'second' => [
'a' => ['test' => '2'],
'b' => 'test'
], 'b' => ['no test']];
$array2 = [
'1' => 'XX',
'second' => [
'a' => ['test' => '5', 'z' => 5],
'b' => 'test'
],
'test'
];
function compareArrayValues($arrayOne, $arrayTwo, &$diff = [], $reversed = false)
{
foreach ($arrayOne as $key => $val) {
if (!isset($arrayTwo[$key])) {
$diff[$key] = 'MISSING IN ' . ($reversed ? 'FIRST' : 'SECOND');
} else if (is_array($val) && (json_encode($arrayOne[$key]) !== json_encode($arrayTwo[$key]))) {
compareArrayValues($arrayOne[$key], $arrayTwo[$key], $diff[$key], $reversed);
} else if ($arrayOne[$key] !== $arrayTwo[$key]) {
$diff[$key] = 'DIFFERENT';
}
}
}
$diff = [];
$diffSecond = [];
compareArrayValues($array1, $array2, $diff);
compareArrayValues($array2, $array1, $diffSecond, true);
print_r($diff);
print_r($diffSecond);
print_r(array_merge($diff, $diffSecond));
Result:
Array
(
[0] => DIFFERENT
[second] => Array
(
[a] => Array
(
[test] => DIFFERENT
[z] => MISSING IN FIRST
)
)
[b] => MISSING IN SECOND
[1] => DIFFERENT
[2] => MISSING IN FIRST
)
array_diff — Computes the difference of arrays
http://php.net/manual/en/function.array-diff.php
array array_diff ( array $array1 , array $array2 [, array $... ] )
Compares array1 against one or more other arrays and returns the values in array1 that are not present in any of the other arrays.
If you want to check non associative arrays, here is the solution:
$a = ['blog', 'company'];
$b = ['company', 'blog'];
(count(array_unique(array_merge($a, $b))) === count($a)) ? 'Equals' : 'Not Equals';
// Equals
The following solution works with custom equality functions that you can pass as a callback. Note that it doesn't check arrays order.
trait AssertTrait
{
/**
* Determine if two arrays have the same elements, possibly in different orders. Elements comparison function must be passed as argument.
*
* #param array<mixed> $expected
* #param array<mixed> $actual
*
* #throws InvalidArgumentException
*/
public static function assertArraysContainSameElements(array $expected, array $actual, callable $comparisonFunction): void
{
Assert::assertEquals(\count($expected), \count($actual));
self::assertEveryElementOfArrayIsInAnotherArrayTheSameAmountOfTimes($expected, $actual, $comparisonFunction);
self::assertEveryElementOfArrayIsInAnotherArrayTheSameAmountOfTimes($actual, $expected, $comparisonFunction);
}
/**
* #param array<mixed> $needles
* #param array<mixed> $haystack
*
* #throws InvalidArgumentException
*/
private static function assertEveryElementOfArrayIsInAnotherArrayTheSameAmountOfTimes(
array $needles,
array $haystack,
callable $comparisonFunction
): void {
Assert::assertLessThanOrEqual(\count($needles), \count($haystack));
foreach ($needles as $expectedElement) {
$matchesOfExpectedElementInExpected = \array_filter(
$needles,
static fn($element): bool => $comparisonFunction($expectedElement, $element),
);
$matchesOfExpectedElementInActual = \array_filter(
$haystack,
static fn($element): bool => $comparisonFunction($expectedElement, $element),
);
Assert::assertEquals(\count($matchesOfExpectedElementInExpected), \count($matchesOfExpectedElementInActual));
}
}
}
I usually use it in database integrations tests when I want to ensure that the expected elements are returned but I don't care about the sorting.
The proper way to compare whether two arrays are equal is to use strict equality (===), which compares recursively. Existing answers are unable to recursively sort an arbitrary array (array of arbitrary depth and order, containing a mixture of sequential and associative arrays) and hence cannot handle comparisons of arbitrary arrays. Sequential arrays are associative arrays with a sequential key (0,1,2,3...) whereas associative arrays do not have a sequential key.
To sort these arbitrary arrays, we have to:
Traverse downwards towards leaf nodes with no more sub-arrays
Sort sequential arrays by serializing then sorting them (to remove the need of having to use custom comparators)
Sort associative arrays by key
The following code implements the solution described above. Improvements to the code are welcome.
function recur_sort( &$array ) {
foreach ( $array as &$value ) {
if ( is_array( $value ) ) recur_sort( $value );
}
if ( is_sequential_array( $array ) ) {
$array = array_map( function( $el ) { return json_encode( $el ); }, $array );
sort( $array, SORT_STRING );
$array = array_map( function( $el ) { return json_decode( $el, true ); }, $array );
return;
} else {
return ksort( $array );
}
}
function is_sequential_array(Array &$a) {
$n = count($a);
for($i=0; $i<$n; $i++) {
if(!array_key_exists($i, $a)) {
return false;
}
}
return true;
}
Example (in PHPUnit):
//A stricter and recursive assertEqualsCanonicalizing
public function assertSameCanonicalizing( $expected, $actual ) {
recur_sort( $expected );
recur_sort( $actual );
$this->assertSame( $expected, $actual );
}
If you want to check that your arrays have the strictly equal (===) associations of keys and values, you can use the following function:
function array_eq($a, $b) {
// If the objects are not arrays or differ in their size, they cannot be equal
if (!is_array($a) || !is_array($b) || count($a) !== count($b)) {
return false;
}
// If the arrays of keys are not strictly equal (after sorting),
// the original arrays are not strictly equal either
$a_keys = array_keys($a);
$b_keys = array_keys($b);
array_multisort($a_keys);
array_multisort($b_keys);
if ($a_keys !== $b_keys) {
return false;
}
// Comparing values
foreach ($a_keys as $key) {
$a_value = $a[$key];
$b_value = $b[$key];
// Either the objects are strictly equal or they are arrays
// which are equal according to our definition. Otherwise they
// are different.
if ($a_value !== $b_value && !array_eq($a_value, $b_value)) {
return false;
}
}
return true;
}
To compare the values of your arrays, also multidimensional, associative and in any combination:
/**
* #see PHPUnit Assert::assertEqualsCanonicalizing()
* #return true if all keys and values are equal and of the same type,
* irregardless of items or keys order
*/
function array_vals_equal(array $a, array $b): bool {
// sort multi-dimensional recursive
$_deep_sort = function (array $a) use (&$_deep_sort): array{
// sort discarding index association or sort keys, depending on array type
array_is_list($a) ? sort($a) : ksort($a);
return array_map(fn($v) => is_array($v) ? $_deep_sort($v) : $v, $a);
};
// operator === checks that the count, types and order of the elements are the same
return $_deep_sort($a) === $_deep_sort($b);
}
// Test cases
assertEquals(array_vals_equal([1], [1]), true, 'simple eq');
assertEquals(array_vals_equal([0], [false]), false, 'simple eq');
assertEquals(array_vals_equal([0], [null]), false, 'simple eq');
assertEquals(array_vals_equal([0, 1], [1, 0]), true, 'simple eq, diff order');
assertEquals(array_vals_equal([0, 1, 2], [1, 0]), false, 'diff count');
assertEquals(array_vals_equal([0, 1], [0, 1, 2]), false, 'diff count 2');
assertEquals(array_vals_equal([1, 2], [1, 2, 'hello']), false, 'diff count 3');
//
assertEquals(array_vals_equal([1, 2, 2], [2, 1, 1]), false, 'same vals repeated');
assertEquals(array_vals_equal([1, 2, 2], [2, 2, 1]), true, 'same vals, different order');
//
assertEquals(array_vals_equal([1, 2, 3], ['1', '2', '3']), false, 'int should not be eq string');
assertEquals(array_vals_equal([0 => 'a', 1 => 'b'], [0 => 'b', 1 => 'a']), true, 'same vals, diff order');
assertEquals(array_vals_equal(['a', 'b'], [3 => 'b', 5 => 'a']), true, 'same vals, diff indexes');
// associative arrays whose members are ordered differently
assertEquals(array_vals_equal(['aa' => 'a', 'bb' => 'b'], ['bb' => 'b', 'aa' => 'a']), true, 'dict with different order');
assertEquals(array_vals_equal(['aa' => 'a', 'bb' => 'b'], ['aa' => 'a']), false, 'a key is missing');
assertEquals(array_vals_equal(['aa' => 'a', 'bb' => 'b'], ['aa' => 'a', 'zz' => 'b']), false, 'dict same vals diff key');
// nested arrays with keys in different order
assertEquals(array_vals_equal(
['aa' => 'a', 'bb' => ['bb' => 'b', 'aa' => 'a']],
['aa' => 'a', 'bb' => ['aa' => 'a', 'bb' => 'b']]
), true, 'dict multi 2 level, keys in different order');
assertEquals(array_vals_equal(
['aa' => 'a', 'bb' => ['aa2' => 'a', 'bb2' => ['aa3' => 'a', 'bb3' => 'b']]],
['aa' => 'a', 'bb' => ['aa2' => 'a', 'bb2' => ['aa3' => 'a', 'bb3' => 'b']]]
), true, 'dict multi 3 level');
assertEquals(array_vals_equal(
['aa' => 'a', 'bb' => [0, 1]],
['aa' => 'a', 'bb' => [1, 0]]
), true, 'dict multi level, 2^ level sequential in different order');
assertEquals(array_vals_equal([[0, 1], ['a', 'b']], [['b', 'a'], [1, 0]]), true, 'multi level sequential');
If you'd like to generate a detailed report, you could use something like this:
function deepCompare(Array $a, Array $b, string $parentAKey, string $parentBKey, bool $compareInverted = true, bool $compareValues = true, string $log = '')
{
foreach ($a as $aKey => $aValue) {
$fullAKey = implode('.', [$parentAKey, $aKey]);
$fullBKey = implode('.', [$parentBKey, $aKey]);
if (! isset($b[$aKey])) {
$log .= "⍰ {$fullAKey} has no equivalent {$fullBKey}\n";
} else {
$bValue = $b[$aKey];
if (is_array($aValue)) {
$log = deepCompare($aValue, $bValue, $fullAKey, $fullBKey, false, $compareValues, $log);
} else {
if ($compareValues) {
if ($aValue != $bValue) {
$log .= "≠ {$fullAKey} value differs from {$fullBKey}\n";
}
}
}
}
}
if ($compareInverted) {
$log = deepCompare($b, $a, $parentBKey, $parentAKey, false, false, $log);
}
return $log;
}
Here is an example for it:
$november = [
'site1' => [
'id' => 15,
'name' => 'Brazil',
'extendedHours' => 454,
],
'site2' => [
'id' => 43,
'name' => 'Portugal',
'extendedHours' => 448,
],
'site3' => [
'id' => 49,
'name' => 'Spain',
'extendedHours' => 0,
],
'totalExtendedHours' => 902,
];
$december = [
'site1' => [
'id' => 15,
'name' => 'Brazil',
'extendedHours' => 498,
],
'site2' => [
'id' => 43,
'name' => 'Portugal',
'extendedHours' => 409,
'extraRequests' => 6,
],
'totalExtendedHours' => 907,
'totalExtraRequests' => 6,
];
echo deepCompare(
$november, -- origin array
$december, -- target array
'Nov2022', -- descriptive name of origin array
'Dec2022', -- descriptive name of target array
true, -- should also compare arrays in reverse order?
true -- should care about array values? (false = names only)
);
This example will output:
≠ Nov2022.site1.extendedHours value differs from Dec2022.site1.extendedHours
≠ Nov2022.site2.extendedHours value differs from Dec2022.site2.extendedHours
⍰ Nov2022.site3 has no equivalent Dec2022.site3
≠ Nov2022.totalExtendedHours value differs from Dec2022.totalExtendedHours
⍰ Dec2022.site2.extraRequests has no equivalent Nov2022.site2.extraRequests
⍰ Dec2022.totalExtraRequests has no equivalent Nov2022.totalExtraRequests
I hope that helps someone.
Use php function array_diff(array1, array2);
It will return a the difference between arrays. If its empty then they're equal.
example:
$array1 = array(
'a' => 'value1',
'b' => 'value2',
'c' => 'value3'
);
$array2 = array(
'a' => 'value1',
'b' => 'value2',
'c' => 'value4'
);
$diff = array_diff(array1, array2);
var_dump($diff);
//it will print array = (0 => ['c'] => 'value4' )
Example 2:
$array1 = array(
'a' => 'value1',
'b' => 'value2',
'c' => 'value3',
);
$array2 = array(
'a' => 'value1',
'b' => 'value2',
'c' => 'value3',
);
$diff = array_diff(array1, array2);
var_dump($diff);
//it will print empty;

How to add an array value to the middle of an associative array?

Lets say I have this array:
$array = array('a'=>1,'z'=>2,'d'=>4);
Later in the script, I want to add the value 'c'=>3 before 'z'. How can I do this?
Yes, the order is important. When I run a foreach() through the array, I do NOT want this newly added value added to the end of the array. I am getting this array from a mysql_fetch_assoc()
The keys I used above are placeholders. Using ksort() will not achieve what I want.
http://www.php.net/manual/en/function.array-splice.php#88896 accomplishes what I'm looking for but I'm looking for something simpler.
Take a sample db table with about 30 columns. I get this data using mysql_fetch_assoc(). In this new array, after column 'pizza' and 'drink', I want to add a new column 'full_dinner' that combines the values of 'pizza' and 'drink' so that when I run a foreach() on the said array, 'full_dinner' comes directly after 'drink'
Am I missing something?
$key = 'z';
$offset = array_search($key, array_keys($array));
$result = array_merge
(
array_slice($array, 0, $offset),
array('c' => 3),
array_slice($array, $offset, null)
);
Handling of nonexistent keys (appending $data by default):
function insertBeforeKey($array, $key, $data = null)
{
if (($offset = array_search($key, array_keys($array))) === false) // if the key doesn't exist
{
$offset = 0; // should we prepend $array with $data?
$offset = count($array); // or should we append $array with $data? lets pick this one...
}
return array_merge(array_slice($array, 0, $offset), (array) $data, array_slice($array, $offset));
}
Demo:
$array = array('a' => 1, 'z' => 2, 'd' => 4);
// array(4) { ["a"]=> int(1) ["c"]=> int(3) ["z"]=> int(2) ["d"]=> int(4) }
var_dump(insertBeforeKey($array, 'z', array('c' => 3)));
// array(4) { ["a"]=> int(1) ["z"]=> int(2) ["d"]=> int(4) ["c"]=> int(3) }
var_dump(insertBeforeKey($array, 'y', array('c' => 3)));
A simple approach to this is to iterate through the original array, constructing a new one as you go:
function InsertBeforeKey( $originalArray, $originalKey, $insertKey, $insertValue ) {
$newArray = array();
$inserted = false;
foreach( $originalArray as $key => $value ) {
if( !$inserted && $key === $originalKey ) {
$newArray[ $insertKey ] = $insertValue;
$inserted = true;
}
$newArray[ $key ] = $value;
}
return $newArray;
}
Then simply call
$array = InsertBeforeKey( $array, 'd', 'c', 3 );
According to your original question the best answer I can find is this:
$a = array('a'=>1,'z'=>2,'d'=>4);
$splitIndex = array_search('z', array_keys($a));
$b = array_merge(
array_slice($a, 0, $splitIndex),
array('c' => 3),
array_slice($a, $splitIndex)
);
var_dump($b);
array(4) {
["a"]=>
int(1)
["c"]=>
int(3)
["z"]=>
int(2)
["d"]=>
int(4)
}
Depending on how big your arrays are you will duplicate quite some data in internal memory, regardless if you use this solution or another.
Furthermore your fifth edit seems to indicate that alternatively your SQL query could be improved. What you seem to want to do there would be something like this:
SELECT a, b, CONCAT(a, ' ', b) AS ab FROM ... WHERE ...
If changing your SELECT statement could make the PHP solution redundant, you should definitely go with the modified SQL.
function insertValue($oldArray, $newKey, $newValue, $followingKey) {
$newArray = array ();
foreach (array_keys($oldArray) as $k) {
if ($k == $followingKey)
$newArray[$newKey] = $newValue;
$newArray[$k] = $oldArray [$k];
}
return $newArray;
}
You call it as
insertValue($array, 'c', '3', 'z')
As for Edit 5:
edit your sql, so that it reads
SELECT ..., pizza, drink, pizza+drink as full_meal, ... FROM ....
and you have the column automatically:
Array (
...
'pizza' => 12,
'drink' => 5,
'full_meal' => 17,
...
)
Associative arrays are not ordered, so you can simply add with $array['c'] = 3.
If order is important, one option is switch to a data structure more like:
$array = array(
array('a' => 1),
array('b' => 2)
array('d' => 4)
);
Then, use array_splice($array, 2, 0, array('c' => 3)) to insert at position 2. See manual on array_splice.
An alternative approach is to supplement the associative array structure with an ordered index that determines the iterative order of keys. For instance:
$index = array('a','b','d');
// Add new value and update index
$array['c'] = 3;
array_splice($index, 2, 0, 'c');
// Iterate the array in order
foreach $index as $key {
$value = $array[$key];
}
You can define your own sortmap when doing a bubble-sort by key. It's probably not terribly efficient but it works.
<pre>
<?php
$array = array('a'=>1,'z'=>2,'d'=>4);
$array['c'] = 3;
print_r( $array );
uksort( $array, 'sorter' );
print_r( $array );
function sorter( $a, $b )
{
static $ordinality = array(
'a' => 1
, 'c' => 2
, 'z' => 3
, 'd' => 4
);
return $ordinality[$a] - $ordinality[$b];
}
?>
</pre>
Here's an approach based on ArrayObject using this same concept
$array = new CitizenArray( array('a'=>1,'z'=>2,'d'=>4) );
$array['c'] = 3;
foreach ( $array as $key => $value )
{
echo "$key: $value <br>";
}
class CitizenArray extends ArrayObject
{
static protected $ordinality = array(
'a' => 1
, 'c' => 2
, 'z' => 3
, 'd' => 4
);
function offsetSet( $key, $value )
{
parent::offsetSet( $key, $value );
$this->uksort( array( $this, 'sorter' ) );
}
function sorter( $a, $b )
{
return self::$ordinality[$a] - self::$ordinality[$b];
}
}
For the moment the best i can found to try to minimize the creation of new arrays are these two functions :
the first one try to replace value into the original array and the second one return a new array.
// replace value into the original array
function insert_key_before_inplace(&$base, $beforeKey, $newKey, $value) {
$index = 0;
foreach($base as $key => $val) {
if ($key==$beforeKey) break;
$index++;
}
$end = array_splice($base, $index, count($base)-$index);
$base[$newKey] = $value;
foreach($end as $key => $val) $base[$key] = $val;
}
$array = array('a'=>1,'z'=>2,'d'=>4);
insert_key_before_inplace($array, 'z', 'c', 3);
var_export($array); // array ( 'a' => 1, 'c' => 3, 'z' => 2, 'd' => 4, )
// create new array
function insert_key_before($base, $beforeKey, $newKey, $value) {
$index = 0;
foreach($base as $key => $val) {
if ($key==$beforeKey) break;
$index++;
}
$end = array_splice($base, $index, count($base)-$index);
$base[$newKey] = $value;
return $base+$end;
}
$array = array('a'=>1,'z'=>2,'d'=>4);
$newArray=insert_key_before($array, 'z', 'c', 3);
var_export($array); // ( 'a' => 1, 'z' => 2, 'd' => 4, )
var_export($newArray); // array ( 'a' => 1, 'c' => 3, 'z' => 2, 'd' => 4, )
function putarrayelement(&$array, $arrayobject, $elementposition, $value = null) {
$count = 0;
$return = array();
foreach ($array as $k => $v) {
if ($count == $elementposition) {
if (!$value) {
$value = $count;
}
$return[$value] = $arrayobject;
$inserted = true;
}
$return[$k] = $v;
$count++;
}
if (!$value) {
$value = $count;
}
if (!$inserted){
$return[$value];
}
$array = $return;
return $array;
}
$array = array('a' => 1, 'z' => 2, 'd' => 4);
putarrayelement($array, '3', 1, 'c');
print_r($array);
Great usage of array functions but how about this as a simpler way:
Add a static column to the SQL and then replace it in the resultant array. Order stays the same:
SQL :
Select pizza , drink , 'pizza-drink' as 'pizza-drink' , 28 columns..... From Table
Array :
$result['pizza-drink'] = $result['pizza'] . $result['drink'];
A simplified Alix Axel function if you need to just insert data in nth position:
function array_middle_push( array $array, int $position, array $data ): array {
return array_merge( array_slice( $array, 0, $position ), $data, array_slice( $array, $position ) );
}
Try this
$array['c']=3;
An associative array is not ordered by default, but if you wanted to sort them alphabetically you could use ksort() to sort the array by it's key.
If you check out the PHP article for ksort() you will se it's easy to sort an array by its key, for example:
<?php
$fruits = array("d"=>"lemon", "a"=>"orange", "b"=>"banana", "c"=>"apple");
ksort($fruits);
foreach ($fruits as $key => $val) {
echo "$key = $val\n";
}
?>
// The above example will output:
a = orange
b = banana
c = apple
d = lemon
you can add it by doing
$array['c']=3;
and if you absolutely want it sorted for printing purposes, you can use php's ksort($array) function
if the keys are not sortable by ksort, then you will have to create your own sort by using php's uasort function. see examples here
http://php.net/manual/en/function.uasort.php

Categories