Comparing nested multidimensional arrays - php

I am trying to compare nested multidimensional arrays by value and key so my scenario is:
I need to compare two nested multidimensional arrays and find the difference in terms of their values by quantity and update those quantities but also items which are in the new array and not the old one for example:
if I have the following data:
First Array:
Array (
[0] => Array ( [name] => hey [qty] => 3 )
[1] => Array ( [name] => hello [qty] => 1 )
[2] => Array ( [name] => test [qty] => 1 )
)
And another nested multidimensional array with the following values:
Second Array:
Array (
[0] => Array ( [name] => hey [qty] => 5 )
[1] => Array ( [name] => hello [qty] => 5 )
[2] => Array ( [name] => PHP [qty] => 2 )
)
I am trying to achieve the following output with only the new items by key in second array and update their values based upon the difference between the first and second array item's quantities i.e.:
Desired output
Array (
[0] => Array ( [name] => hey [qty] => 2 )
[1] => Array ( [name] => hello [qty] => 4 )
[2] => Array ( [name] => PHP [qty] => 2 )
)
I am producing the difference using the following but note how PHP is not added. I am not too sure how to check for it in my inner for loop without adding it.
<?php
$items = [
'hey',
'hey',
'hey',
'hello',
'test'
];
$arr = array_count_values($items);
$oldItems = array();
foreach ($arr as $name => $qty) {
$oldItems[] = compact('name', 'qty');
}
$newItems = [
['name' => 'hey', 'qty' => 5],
['name' => 'hello', 'qty'=> 5],
['name' => 'PHP', 'qty' => 2]
];
$diff = [];
foreach($newItems as $newItem) {
foreach($oldItems as $oldItem) {
if ($newItem['name'] == $oldItem['name']) {
//get quantity
$qtyDiff = $newItem['qty'] - $oldItem['qty'];
if ($qtyDiff > 0) {
$diff[$newItem['name']] = $qtyDiff;
}
}
}
}
print_r($diff); die();
My current output from this script is as follows:
Array (
[0] => Array ( [name] => hey [qty] => 2 )
[1] => Array ( [name] => hello [qty] => 4 )
)
Any help is appreciated or feedback on improvements. Thanks!

Only need one foreach:
<?php
$oldItems = [
['name' => 'hey', 'qty' => 3],
['name' => 'hello', 'qty'=> 1],
['name' => 'test', 'qty' => 1]
];
$newItems = [
['name' => 'hey', 'qty' => 5],
['name' => 'hello', 'qty'=> 5],
['name' => 'PHP', 'qty' => 2]
];
$diff = array();
foreach ($oldItems as $id => $oldRow) {
$newRow = $newItems[$id];
if ($newRow['name'] == $oldRow['name']) {
$diff[] = array(
'name' => $oldRow['name'],
'qty' => $newRow['qty'] - $oldRow['qty']
);
} else {
$diff[] = $newItems[$id];
}
}
print_r($diff);
Output:
Array
(
[0] => Array
(
[name] => hey
[qty] => 2
)
[1] => Array
(
[name] => hello
[qty] => 4
)
[2] => Array
(
[name] => PHP
[qty] => 2
)
)
If the order or size of old and new items are different, then use the following:
<?php
$oldItems = [
['name' => 'hey', 'qty' => 3],
['name' => 'hello', 'qty'=> 1],
['name' => 'test', 'qty' => 1]
];
$newItems = [
['name' => 'hey', 'qty' => 5],
['name' => 'hello', 'qty'=> 5],
['name' => 'PHP', 'qty' => 2]
];
$name2newItem = array();
foreach ($newItems as $item) {
$name2newItem[$item['name']] = $item;
}
$diff = array();
foreach ($oldItems as $id => $oldRow) {
$name = $oldRow['name'];
if (isset($name2newItem[$name])) {
$newRow = $name2newItem[$name];
$diff[$name] = array(
'name' => $name,
'qty' => $newRow['qty'] - $oldRow['qty']
);
}
}
foreach ($name2newItem as $name => $item) {
if (!isset($diff[$name])) {
$diff[$name] = $item;
}
}
$diff = array_values($diff);
print_r($diff);

Related

Summation of array elements by the indexer [duplicate]

I have an array of subarrays in the following format:
[
'a' => ['id' => 20, 'name' => 'chimpanzee'],
'b' => ['id' => 40, 'name' => 'meeting'],
'c' => ['id' => 20, 'name' => 'dynasty'],
'd' => ['id' => 50, 'name' => 'chocolate'],
'e' => ['id' => 10, 'name' => 'bananas'],
'f' => ['id' => 50, 'name' => 'fantasy'],
'g' => ['id' => 50, 'name' => 'football']
]
And I would like to group it into a new array based on the id field in each subarray.
array
(
10 => array
(
e => array ( id = 10, name = bananas )
)
20 => array
(
a => array ( id = 20, name = chimpanzee )
c => array ( id = 20, name = dynasty )
)
40 => array
(
b => array ( id = 40, name = meeting )
)
50 => array
(
d => array ( id = 50, name = chocolate )
f => array ( id = 50, name = fantasy )
g => array ( id = 50, name = football )
)
)
$arr = array();
foreach ($old_arr as $key => $item) {
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
foreach($array as $key => $value){
$newarray[$value['id']][$key] = $value;
}
var_dump($newarray);
piece of cake ;)
The following code adapts #Tim Cooper’s code to mitigate Undefined index: id errors in the event that one of the inner arrays doesn’t contain an id:
$arr = array();
foreach($old_arr as $key => $item)
{
if(array_key_exists('id', $item))
$arr[$item['id']][$key] = $item;
}
ksort($arr, SORT_NUMERIC);
However, it will drop inner arrays without an id.
E.g.
$old_arr = array(
'a' => array ( 'id' => 20, 'name' => 'chimpanzee' ),
'b' => array ( 'id' => 40, 'name' => 'meeting' ),
'c' => array ( 'id' => 20, 'name' => 'dynasty' ),
'd' => array ( 'id' => 50, 'name' => 'chocolate' ),
'e' => array ( 'id' => 10, 'name' => 'bananas' ),
'f' => array ( 'id' => 50, 'name' => 'fantasy' ),
'g' => array ( 'id' => 50, 'name' => 'football' ),
'h' => array ( 'name' => 'bob' )
);
will drop the 'h' array completely.
You can also use Arrays::groupBy() from ouzo-goodies:
$groupBy = Arrays::groupBy($array, Functions::extract()->id);
print_r($groupBy);
And result:
Array
(
[20] => Array
(
[0] => Array
(
[id] => 20
[name] => chimpanzee
)
[1] => Array
(
[id] => 20
[name] => dynasty
)
)
[40] => Array
(
[0] => Array
(
[id] => 40
[name] => meeting
)
)
[50] => Array
(
[0] => Array
(
[id] => 50
[name] => chocolate
)
[1] => Array
(
[id] => 50
[name] => fantasy
)
[2] => Array
(
[id] => 50
[name] => football
)
)
[10] => Array
(
[0] => Array
(
[id] => 10
[name] => bananas
)
)
)
And here are the docs for Arrays and Functions.
Here is a function that will take an array as the first argument and a criteria (a string or callback function) as the second argument. The function returns a new array that groups the array as asked for.
/**
* Group items from an array together by some criteria or value.
*
* #param $arr array The array to group items from
* #param $criteria string|callable The key to group by or a function the returns a key to group by.
* #return array
*
*/
function groupBy($arr, $criteria): array
{
return array_reduce($arr, function($accumulator, $item) use ($criteria) {
$key = (is_callable($criteria)) ? $criteria($item) : $item[$criteria];
if (!array_key_exists($key, $accumulator)) {
$accumulator[$key] = [];
}
array_push($accumulator[$key], $item);
return $accumulator;
}, []);
}
Here is the given array:
$arr = array(
'a' => array ( 'id' => 20, 'name' => 'chimpanzee' ),
'b' => array ( 'id' => 40, 'name' => 'meeting' ),
'c' => array ( 'id' => 20, 'name' => 'dynasty' ),
'd' => array ( 'id' => 50, 'name' => 'chocolate' ),
'e' => array ( 'id' => 10, 'name' => 'bananas' ),
'f' => array ( 'id' => 50, 'name' => 'fantasy' ),
'g' => array ( 'id' => 50, 'name' => 'football' )
);
And examples using the function with a string and a callback function:
$q = groupBy($arr, 'id');
print_r($q);
$r = groupBy($arr, function($item) {
return $item['id'];
});
print_r($r);
The results are the same in both examples:
Array
(
[20] => Array
(
[0] => Array
(
[id] => 20
[name] => chimpanzee
)
[1] => Array
(
[id] => 20
[name] => dynasty
)
)
[40] => Array
(
[0] => Array
(
[id] => 40
[name] => meeting
)
)
[50] => Array
(
[0] => Array
(
[id] => 50
[name] => chocolate
)
[1] => Array
(
[id] => 50
[name] => fantasy
)
[2] => Array
(
[id] => 50
[name] => football
)
)
[10] => Array
(
[0] => Array
(
[id] => 10
[name] => bananas
)
)
)
Passing the callback is overkill in the example above, but using the callback finds its use when you pass in an array of objects, a multidimensional array, or have some arbitrary thing you want to group by.
Maybe it's worth to mention that you can also use php array_reduce function
$items = [
['id' => 20, 'name' => 'chimpanzee'],
['id' => 40, 'name' => 'meeting'],
['id' => 20, 'name' => 'dynasty'],
['id' => 50, 'name' => 'chocolate'],
['id' => 10, 'name' => 'bananas'],
['id' => 50, 'name' => 'fantasy'],
['id' => 50, 'name' => 'football'],
];
// Grouping
$groupedItems = array_reduce($items, function ($carry, $item) {
$carry[$item['id']][] = $item;
return $carry;
}, []);
// Sorting
ksort($groupedItems, SORT_NUMERIC);
print_r($groupedItems);
https://www.php.net/manual/en/function.array-reduce.php
Because of how PHP's sorting algorithm treats multidimensional arrays -- it sorts by size, then compares elements one at a time, you can actually use a key-preserving sort on the input BEFORE restructuring. In functional style programming, this means that you don't need to declare the result array as a variable.
Code: (Demo)
asort($array);
var_export(
array_reduce(
array_keys($array),
function($result, $k) use ($array) {
$result[$array[$k]['id']][$k] = $array[$k];
return $result;
}
)
);
I must say that functional programming is not very attractive for this task because the first level keys must be preserved.
Although array_walk() is more succinct, it still requires the result array to be passed into the closure as a reference variable. (Demo)
asort($array);
$result = [];
array_walk(
$array,
function($row, $k) use (&$result) {
$result[$row['id']][$k] = $row;
}
);
var_export($result);
I'd probably recommend a classic loop for this task. The only thing the loop needs to do is rearrange the first and second level keys. (Demo)
asort($array);
$result = [];
foreach ($array as $k => $row) {
$result[$row['id']][$k] = $row;
}
var_export($result);
To be completely honest, I expect that ksort() will be more efficient than pre-loop sorting, but I wanted to a viable alternative.

sum of unique values in array

I have below array $billitems_taxes
[0] => Array
(
[id] => 1
[tax_name] => A
[tax_value] => 12
)
[1] => Array
(
[id] => 2
[tax_name] => A
[tax_value] => 8
)
[2] => Array
(
[id] => 3
[tax_name] => B
[tax_value] => 12
)
and I want output as below, find two common tax_name and do some of same and then create a new array.
[0] => Array
(
[id] => 1
[tax_name] => A
[tax_value] => 20
)
[1] => Array
(
[id] => 3
[tax_name] => B
[tax_value] => 12
)
I tried with below code, but it did not return a correct array.
$return_array = [];
foreach($billitems_taxes as $b)
{
$return_array['tax_name'] = $b->tax_name;
$return_array['tax_value'] += $b->tax_value;
}
First off, you have an array of arrays, not objects.
Then your loop needs to know if it has already seen a this tax name which will already be in the new array to check that I used array_key_exists()
$return_array = [];
foreach($billitems_taxes as $b)
{
if ( array_key_exists($b['tax_name'], $return_array) ) {
$return_array[$b['tax_name']]['tax_value'] += $b['tax_value'];
} else {
$return_array[$b['tax_name']] = $b;
}
}
RESULT
Array(
[A] => Array
([id] => 1
[tax_name] => A
[tax_value] => 20
)
[B] => Array
([id] => 3
[tax_name] => B
[tax_value] => 12
)
)
And if its important for the array to be numerically indexed just add
$return_array = array_values($return_array);
after the end of the loop
You must group by 'tax_name' and must sum 'tax_value'.
$billitems_taxes = [
['id' => 1, 'tax_name' => 'A', 'tax_value' => 12],
['id' => 2, 'tax_name' => 'A', 'tax_value' => 8],
['id' => 3, 'tax_name' => 'B', 'tax_value' => 12]
];
$result = [];
foreach($billitems_taxes as $row){
$groupKey = $row['tax_name'];
if(array_key_exists($groupKey,$result)){
$result[$groupKey]['tax_value'] += $row['tax_value'];
} else {
$result[$groupKey] = $row;
}
}
$result = array_values($result);
echo '<pre>';
var_export($result);
/*
array (
0 =>
array (
'id' => 1,
'tax_name' => 'A',
'tax_value' => 20,
),
1 =>
array (
'id' => 3,
'tax_name' => 'B',
'tax_value' => 12,
),
)
*/
The solution with the external class tableArray is very simple. The result is the same.
$result = tableArray::create($billitems_taxes)
->filterGroupAggregate(['tax_value' => 'SUM'],['tax_name'])
->fetchAll()
;

How to get Php multidimensional array same key’s same value’s related total in new array?

Php multidimensional array same key’s same value’s related total in
new array. I have an array of following mentioned. i need new array
as total qty of same item_id. anyone can help would be appreciate.
My Original Array is as following
Array
(
[a] => Array
(
[item] => Array
(
[item_id] => 1
)
[qty] => 0
),
[b] => Array
(
[item] => Array
(
[item_id] => 2
)
[qty] => 35
),
[c] => Array
(
[item] => Array
(
[item_id] => 2
)
[qty] => 15
),
[e] => Array
(
[item] => Array
(
[item_id] => 3
)
[qty] => 20
),
);
I want array Output like following :
Array(
[0] => Array (
[item_id] => 1,
[item_total_qty] => 0,
)
[1] => Array (
[item_id] => 2,
[item_total_qty] => 50,
)
[2] => Array (
[item_id] => 3,
[item_total_qty] => 20,
)
);
Hope it help
$arrays = array(
'a' => array(
'item' => array(
'item_id' => 1
),
'qty' => 0
),
'b' => array(
'item' => array(
'item_id' => 2
),
'qty' => 35
),
'c' => array(
'item' => array(
'item_id' => 2
),
'qty' => 15
),
'd' => array(
'item' => array(
'item_id' => 3
),
'qty' => 20
)
);
$result = array();
foreach ($arrays as $key => $array) {
if (is_array($result) && !empty($result)) {
foreach ($result as $key => $r) {
if ($r['item_id'] == $array['item']['item_id']) {
$result[$key]['item_total_qty'] += $array['qty'];
continue 2;
}
}
$result[] = array(
'item_id' => $array['item']['item_id'],
'item_total_qty' => $array['qty']);
} else {
$result[] = array(
'item_id' => $array['item']['item_id'],
'item_total_qty' => $array['qty']);
}
}
Simple foreach on your original table:
$sorted = array();
foreach ($original as $item) {
$id = $item['item']['item_id'];
$sorted[$id]['item_total_qty'] = $sorted[$id] ? $sorted[$id] + $item['qty'] : item['qty'];
$sorted[$id]['item_id'] = $id;
}
$sorted = array_values($sorted);

PHP - Merge 2 multidimensional array based on value

Currently, I have 2 multidimensional array and I'm looking to combine them into one giant array where the value's name in array 1 matches the value's name in array 2.
The array's look as followed...
Array1
(
[0] => Array
(
[id] => 1
[name] => test1
[desc] => test_desc
[quantity] => 3
)
[1] => Array
(
[id] => 2
[name] => test2
[desc] => test_desc
[quantity] => 33
)
)
Array2
(
[0] => Array
(
[holder] => 'John'
[name] => test1
[desc] => test_desc
[location] => ATL
)
[1] => Array
(
[holder] => 'Jackie'
[name] => test3
[desc] => test_desc
[location] => SF
)
)
I'm looking to merge the arrays where the 'name' column in array1 matches in array2 and also combine the columns in array's 1 & 2 into the final array. This should look like...
FinalArray
(
[0] => Array
(
[id] => 1
[holder] => 'John'
[name] => test1
[desc] => test_desc
[location] => ATL
[quantity] => 3
)
[1] => Array
(
[holder] => 'Jackie'
[name] => test3
[desc] => test_desc
[location] => SF
)
[2] => Array
(
[id] => 2
[name] => test2
[desc] => test_desc
[quantity] => 33
)
)
Where the "test1" combines the different columns across the 2 arrays into a new array inside the "FinalArray". I've tried researching some ideas with array_merge and array_merge_recursive but I'm not entirely sure if I'm going in the correct direction. Thanks in advance.
Try like this
$array1=[['id' => 1,'name' => 'test1','desc' => 'test_desc','quantity' => 3],
['id' => 2,'name' => 'test2','desc' => 'test_desc','quantity' => 33]];
$array2=[['holder' => 'John','name' => 'test1','desc' => 'test_desc','location' => 'ATL'],
['holder' => 'Jackie','name' => 'test3','desc' => 'test_desc','location' => 'SF']];
$final=[];
foreach ($array1 as $key1=>$data1){
foreach ($array2 as $key2=>$data2){
if($data1['name']==$data2['name']){
$final[]=$data1+$data2;
unset($array1[$key1]);
unset($array2[$key2]);
}
}
}
if(!empty($array1)){
foreach ($array1 as $value){
$final[]=$value;
}
}
if(!empty($array2)){
foreach ($array2 as $value){
$final[]=$value;
}
}
It will give output as
One more solution
function merge_by_name(array $arr1, array $arr2) {
$result = [];
foreach ($arr1 as $value) {
$key = array_search($value['name'], array_column($arr2, 'name'));
if($key !== false) {
$result[] = array_merge($value, $arr2[$key]);
unset($arr2[$key]);
} else {
$result[] = $value;
}
}
$result = array_merge($result, $arr2);
return $result;
}
Test
$arr1 = [
[
'id' => 1,
'name' => 'test1',
'desc' => 'test_desc',
'quantity' => 3
],
[
'id' => 2,
'name' => 'test2',
'desc' => 'test_desc',
'quantity' => 33
],
];
$arr2 = [
[
'holder' => 'John',
'name' => 'test1',
'desc' => 'test_desc',
'location' => 'ATL'
],
[
'holder' => 'Jackie',
'name' => 'test3',
'desc' => 'test_desc',
'location' => 'SF'
],
];
var_export(merge_by_name($arr1, $arr2));
Result
array (
0 =>
array (
'id' => 1,
'name' => 'test1',
'desc' => 'test_desc',
'quantity' => 3,
'holder' => 'John',
'location' => 'ATL',
),
1 =>
array (
'id' => 2,
'name' => 'test2',
'desc' => 'test_desc',
'quantity' => 33,
),
2 =>
array (
'holder' => 'Jackie',
'name' => 'test3',
'desc' => 'test_desc',
'location' => 'SF',
),
)

php put two array indexs in one subarray if have a same value

we have an array like this :
Array
(
[0] => Array
(
[value] => aaa
[parent_id] => 5
)
[1] => Array
(
[value] => bbb
[parent_id] => 3
)
[2] => Array
(
[value] => ccc
[parent_id] => 3
)
)
as you can see 2 index have same [parent_id] , we need convert this array to this
Array
(
[0] => Array
(
[parent_id] => 5
[sub] => Array(
[0] =>(
[value] => aaa
)
)
)
[1] => Array
(
[parent_id] => 3
[sub] => Array(
[0] =>(
[value] => bbb
)
[1] =>(
[value] => ccc
)
)
)
)
in php we used this functions :
foreach ($Array as $item) {
$item['subs'] = array();
$indexedItems[$item['parent_id']] = (object) $item;
}
for($i=0; $i<count($Array); $i++){
if($Array[$i]['parent_id'] == $Array[$i-1]['parent_id']){
$indexedItems[$item['parent_id']]->subs[]=$Array[$i]['value'];
}
}
but it doesnot works , can you help us to do that , please ?
If u realy want that complicated array you gave as example you have to track the parent_id.
This is how :
<?php
$foo = [
[
'value' => 'aaa',
'parent_id' => 900,
],
[
'value' => 'aaa',
'parent_id' => 813,
],
[
'value' => 'aaa',
'parent_id' => 900,
],
[
'value' => 'aaa',
'parent_id' => 813,
],
];
$indexed = [];
foreach($foo as $f) {
$index = getParentIndex($indexed, $f['parent_id']);
if ($index === null) {
$indexed[] = [
'parent_id' => $f['parent_id'],
'subs' => [
['value' => $f['value'] ],
],
];
}else{
$indexed[$index]['subs'][] = [ 'value' => $f['value'], ];
}
}
function getParentIndex($array, $parent_id) {
for($i=0;$i<count($array);$i++) {
if ($array[$i]['parent_id'] == $parent_id) return $i;
}
return null;
}
var_dump($indexed);
However, this looks a very complicated array to work with imho.
I would suggest the following snippet :
$indexed = [];
foreach($foo as $f) {
if (!isset($indexed[$f['parent_id']])) $indexed[$f['parent_id']] = [];
$indexed[$f['parent_id']][] = $f['value'];
}
var_dump($indexed);
This will output an array similar to :
array (size=2)
900 =>
array (size=2)
0 => string 'aaa' (length=3)
1 => string 'aaa' (length=3)
813 =>
array (size=2)
0 => string 'aaa' (length=3)
1 => string 'aaa' (length=3)

Categories