Merge two Arrays by Values - php

I have this Array:
$mergedItems = array(
0 => array(
'id_item' => 'AZ-110'
'amount' => 12
),
1 => array(
'id_item' => 'BZ-110',
'amount' => 13
),
2 => array(
'id_item' => 'BZ-210',
'amount' => 28
),
3 => array(
'id_item' => 'CZ-291',
'amount' => 11
)
);
AND this Array:
$items = array(
0 => array(
'number' => 'AZ-110'
),
1 => array(
'number' => 'BZ-110'
),
2 => array(
'number' => 'CZ-291'
),
3 => array(
'number' => 'BZ-210'
)
);
Now what i want is to order the first array by the id_item Value to match the same order than the 2nd one by its values.
The resulting array has to include all values of the 2nd array AND the belonging amount-value of the first array. The Keys must not be kept!
I can't use array_merge since the 2nd Array has a dynamic amount of more items, so i only want all items from the second Array that are set in the first one.
Does anyone get what i mean? I am searching for a quick and non-dirty way to get this result as expected.
/Edit:
Expected Array:
$detailedItems = array(
0 => array(
'number' => 'AZ-110',
'amount' => 12
),
1 => array(
'number' => 'BZ-110',
'amount' => 13
),
2 => array(
'number' => 'CZ-291',
'amount' => 11
),
3 => array(
'number' => 'BZ-210',
'amount' => 28
)
);

A PHP 5.5 solution:
$itemMap = array_flip(array_column($mergedItems, 'id_item'));
$result = array_map(
function($i) use($itemMap, $mergedItems) {
return $mergedItems[$itemMap[$i['number']]];
},
$items);
print_r($result);
For 5.3 <= PHP < 5.5 you can simply substitute array_map for array_column:
$itemMap = array_flip(array_map(
function($i) { return $i['id_item']; },
$mergedItems));
How it works
The idea is to create a map of item numbers to indexes inside $mergedItems, ie.
[
'AZ-100' => 0,
'BZ-110' => 1,
'BZ-210' => 2,
// etc
]
With this information at hand it's very easy to iterate over $items (so that the result will be ordered based on that array) and pick the appropriate element from $mergedItems to append to the result each time.

$temp = $items;
foreach($temp as &$val)
{
foreach($mergedItems as $item)
{
if($item['id_item'] == $val['number'])
{
$val['amount'] = $item['amount'];
break;
}
}
}
print_r($temp);

There isn't really a "non-dirty" (meaning single line) way to do this as far as I know, but this function should work:
$out = array();
foreach ($mergedItems as $key => $value) {
if (array_key_exists($key, $detailedItems)) { // Make sure it exists to prevent errors
$out[$key] = $detailedItems[$key] + array('amount' => $value['amount']);
} else {
$out[$key] = $value['amount'];
}
}
print_r($out);

You can try following codes:
foreach ($mergedItems as $item) {
$merged[$item['id_item']] = array('amount' => $item['amount']);
}
foreach ($items as $item)
{
$detailedItems[] = array_merge($item, $merged[$item['number']]);
}
Output
var_dump($detailedItems);
array (size=4)
0 =>
array (size=2)
'number' => string 'AZ-110' (length=6)
'amount' => int 12
1 =>
array (size=2)
'number' => string 'BZ-110' (length=6)
'amount' => int 13
2 =>
array (size=2)
'number' => string 'CZ-291' (length=6)
'amount' => int 11
3 =>
array (size=2)
'number' => string 'BZ-210' (length=6)
'amount' => int 28

Related

Remove previous iteration's data from re-used array

I have two arrays, $array1 and $array2. $array2 is an array of arrays. For each subarray of $array2, I want to print $array1 but with additional entries that depend on whether the $array2 subarray has keys "a" or "c" with value 1. My code prints $array1 each loop, but there are additional entries in $array1 for the later iterations that I wasn't expecting. Why do I get these entries, and how do I prevent them?
Sample code:
$array1 = array(
"service" => "coding",
"data" => array(
"ITEM" => array(
array(
"CODE" => "9999", //STANDARD
"QUANTITY" => 1
),
)
)
);
$array2 = array(
array(
"a" => "1",
"b" => "1",
"c" => "1",
"d" => "1",
),
array(
"cancel" => "1",
"a" => "1",
"b" => "",
"c" => "",
"d" => "1",
),
array(
"cancel" => "1",
"a" => "",
"b" => "1",
"c" => "1",
"d" => "",
),
);
for ($i = 0; $i < count($array2); $i++) {
foreach ($array2[$i] as $key => $value) {
if($key == 'a' && $value == 1){
array_push($array1['data']['ITEM'],
array('SOMETHING' => 'this_is_a',
'ELSE' => "1"
)
);
}
if($key == 'c' && $value == 1){
array_push($array1['data']['ITEM'],
array('SOMETHING' => 'this_is_c',
'ELSE' => "1"
)
);
}
}
echo "Loop #$i result:\n";
var_export($array1);
echo "\n";
}
You can test the above code as a PHP Sandbox snippet.
The actual result is:
Loop #0 result:
array (
'service' => 'coding',
'data' =>
array (
'ITEM' =>
array (
0 =>
array (
'CODE' => '9999',
'QUANTITY' => 1,
),
1 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
2 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
),
),
)
Loop #1 result:
array (
'service' => 'coding',
'data' =>
array (
'ITEM' =>
array (
0 =>
array (
'CODE' => '9999',
'QUANTITY' => 1,
),
1 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
2 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
3 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
),
),
)
Loop #2 result:
array (
'service' => 'coding',
'data' =>
array (
'ITEM' =>
array (
0 =>
array (
'CODE' => '9999',
'QUANTITY' => 1,
),
1 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
2 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
3 =>
array (
'SOMETHING' => 'this_is_a',
'ELSE' => '1',
),
4 =>
array (
'SOMETHING' => 'this_is_c',
'ELSE' => '1',
),
),
),
)
The loop #0 result is correct, but the later loops have additional entries in $array1['data']['ITEM']. Desired result:
Loop #0 result:
array (
'service' => coding
'data' => array (
'ITEM' => array (
0 => array (
'CODE' => 9999
'QUANTITY' => 1
)
1 => array (
'SOMETHING' => 'this_is_a'
'ELSE' => 1
)
2 => array (
'SOMETHING' => 'this_is_c'
'ELSE' => 1
)
)
)
)
Loop #1 result:
array (
'service' => coding
'data' => array (
'ITEM' => array (
0 => array (
'CODE' => 9999
'QUANTITY' => 1
)
1 => array (
'SOMETHING' => 'this_is_a'
'ELSE' => 1
)
)
)
)
Loop #2 result:
array (
'service' => coding
'data' => array (
'ITEM' => array (
0 => array (
'CODE' => 9999
'QUANTITY' => 1
)
1 => array (
'SOMETHING' => 'this_is_c'
'ELSE' => 1
)
)
)
)
You may use array_map to loop through the second array and only push to the first array's ['data']['ITEM'] when the current iteration has a key named a and its value is 1.
$arr1 = [
'service' => 'coding',
'data' => [
'ITEM' => [
[
'CODE' => '9999',
'QUANTITY' => 1
]
]
]
];
$arr2 = [
[
'a' => '1',
'b' => '1',
'c' => '1',
'd' => '1',
],
[
'cancel' => '1',
'a' => '1',
'b' => '',
'c' => '',
'd' => '1',
],
[
'cancel' => '1',
'a' => '',
'b' => '1',
'c' => '1',
'd' => '',
],
];
// loop through the second array ($arr2)
// the "use" is very important here as it let's the callback function to access $arr1 variable
$finalArr = array_map(function ($el) use ($arr1) {
// if in the current iteration a kley named "a" and its value is "1" is found then push it to the first array's ['data']['ITEM'] key.
// $arr1 here is passed by value so the real array won't be channged, we're, more or less, working with copy of $arr1 in the function.
isset($el['a']) && $el['a'] == 1 && ($arr1['data']['ITEM'][] = [
'something' => 'This is a', // tried to keep same output as yours
'else' => $el['a'] // tried to keep same output as yours
]);
// for "c" key
isset($el['c']) && $el['c'] == 1 && ($arr1['data']['ITEM'][] = [
'something' => 'This is c', // tried to keep same output as yours
'else' => $el['c'] // tried to keep same output as yours
]);
// return the $arr1 copy regardless of whether we pushed "a" key or not.
return $arr1;
}, $arr2);
// print the resulting array
print_r($finalArr);
Result (for the sample data):
array(
0 => array(
'service' => 'coding',
'data' => array(
'ITEM' => array(
0 => array(
'CODE' => '9999',
'QUANTITY' => 1,
),
1 => array(
'something' => 'This is a',
'else' => '1',
),
2 => array(
'something' => 'This is c',
'else' => '1',
),
),
),
),
1 => array(
'service' => 'coding',
'data' => array(
'ITEM' => array(
0 => array(
'CODE' => '9999',
'QUANTITY' => 1,
),
1 => array(
'something' => 'This is a',
'else' => '1',
),
),
),
),
2 => array(
'service' => 'coding',
'data' => array(
'ITEM' => array(
0 => array(
'CODE' => '9999',
'QUANTITY' => 1,
),
1 => array(
'something' => 'This is c',
'else' => '1',
),
),
),
),
)
You can learn more about callback functions (anonymous functions) in the PHP manual.
You are not returning $array1['data']['ITEM'] to its original state between each iteration. Just write the following between your for() and your foreach().
$array1['data']['ITEM'] = array_slice($array1['data']['ITEM'], 0, 1);
Because array keys must be unique, you don't need to loop through all elements to see if a or c exists -- this will cost unnecessary cycles.
Futhermore, I find array destructuring to be a good choice to isolate the only two values you seek.
I might recommend this much simpler and more intuitive snippet: (Demo)
foreach ($array2 as $i => ['a' => $a, 'c' => $c]) {
$temp = $array1;
if($a == 1) {
$temp['data']['ITEM'][] = [
'SOMETHING' => 'this_is_a',
'ELSE' => '1'
];
}
if ($c == 1) {
$temp['data']['ITEM'][] = [
'SOMETHING' => 'this_is_c',
'ELSE' => '1'
];
}
echo "Loop #$i result:\n";
var_export($temp);
echo "\n";
}
The unexpected entries are present because $array1 is modified with each iteration (specifically, by array_push). To prevent this, each iteration must operate on a different copy of $array1. Since variable assignment and argument passing will each copy an array, either could be used. Similar to argument passing, closures can inherit (note: not in the OOP sense) variables from outer scopes, binding to their values (which will copy an array), providing a third potential basis for a solution.
The fix requiring the least amount of edits is to assign $array1 to another variable at the start of the loop, and replace $array1 in the rest of the loop with this variable:
for ($i = 0; $i < count($array2); $i++) {
$result = $array1;
// ...
Alternatively, by moving the body of the loop to a function, $array1 becomes a copy within the function body:
function process(array $options, array $output) {
// Ensure entries to test are present:
$options += ['a' => NULL, 'c' => NULL];
/* As an alternative to the above, if entries should be added
* to $output whenever 'a' or 'c' has a truthy value,
* `! empty(...)` could be used instead of the `... == 1`
* tests below.
*/
if ($options['a'] == 1) {
$output['data']['ITEM'][] = [
'SOMETHING' => 'this_is_a',
'ELSE' => 1,
];
}
if ($options['c'] == 1) {
$output['data']['ITEM'][] = [
'SOMETHING' => 'this_is_c',
'ELSE' => 1,
];
}
return $output;
}
foreach ($array2 as $i => $subarray) {
echo "// Loop #$i result:";
var_export( process($subarray, $array1) );
echo ";\n";
}
As a third approach, you could apply array_map to $array2, as is done by ths. For comparison, the preceding foreach loop could be replaced with a call to array_map:
var_export(
array_map(function (array $subarray) use ($array1) {
return process($subarray, $array1);
}, $array2)
);
Or, with an arrow function (which automatically use variables from the outer scope):
var_export(
array_map(
fn (array $subarray) => process($subarray, $array1),
$array2
) );
Note that for arrays that won't be modified (or for which modification won't matter), you can pass them by reference (or bind by reference in closures) to avoid the overhead of copying them.
function process(array &$options, array $output) {
// ...

Counting occurrences in an array that has more than 1 key

I'm trying to get the occurrences of an array taking in count 1 key value, for example:
$newarray[0] = [
'id' => '2',
'date' => '2016-04-22'
];
$newarray[1] = [
'id' => '2',
'date' => '2016-04-13'
];
$newarray[2] = [
'id' => '2',
'date' => '2016-04-12'
];
$newarray[3] = [
'id' => '1',
'date' => '2016-03-11'
];
$newarray[4] = [
'id' => '2',
'date' => '2016-03-05'
];
$newarray[5] = [
'id' => '1',
'date' => '2016-03-01'
];
I want to transform this, to something like this:
Array ( [0] => Array ( [id] => 1 [date] => 2016-03-11 [occurences] => 2 ) [1] => Array ( [id] => 2 [date] => 2016-04-22 [occurences] => 4 ) )
I tried doing this:
$cleanarray;
$newarray2=$newarray;
$newarray;
$k=0;
$num=1;
for($i=0; $i<count($newarray); $i++){
for($j=1; $j<count($newarray2); $j++){
if($newarray2[$j]["id"]==$newarray[$i]["id"]){
$num++;
}
}
$cleanarray[$k] = [
'id' => $newarray[$i]["id"],
'date' => $newarray[$i]["date"],
'occurences' => $num
];
$k++;
$num=0;
}
But a lot of items repeat, with the same occurrences but several times, and in other cases the repeated items (with same id) would have different occurrences, so I don't know what can I do, I know that there is a function of:
$occurences = array_count_values($array);
But it doesn't work in this case, how can I approach to the solution?
I know what are you looking for, but I think this could solve your problems:
$newarray[0] = [
'id' => '2',
'date' => '2016-04-22'
];
$newarray[1] = [
'id' => '2',
'date' => '2016-04-12'
];
$newarray[2] = [
'id' => '2',
'date' => '2016-04-12'
];
$newarray[3] = [
'id' => '1',
'date' => '2016-03-11'
];
$newarray[4] = [
'id' => '2',
'date' => '2016-03-05'
];
$newarray[5] = [
'id' => '1',
'date' => '2016-03-01'
];
foreach($newarray as $key => $value){
if(isset($found[$value['id']][$value['date']])) {
$found[$value['id']][$value['date']]++;
} else {
$found[$value['id']][$value['date']] = 1;
}
}
print_r($found);
this will return something like:-
Array
(
[2] => Array
(
[2016-04-22] => 1
[2016-04-12] => 2
[2016-03-05] => 1
)
[1] => Array
(
[2016-03-11] => 1
[2016-03-01] => 1
)
)
Using temporary keys for this process will be the most performant way. Temporary keys simplify the output array task, requiring less and faster checking. If you wish to sort on id after the result array is generated, the temporary keys allow a simple ksort() call.
Code: (Demo)
$newarray=[
['id' => '2','date' => '2016-04-22'],
['id' => '2','date' => '2016-04-13'],
['id' => '2','date' => '2016-04-12'],
['id' => '1','date' => '2016-03-11'],
['id' => '2','date' => '2016-03-05'],
['id' => '1','date' => '2016-03-01']
];
foreach($newarray as $a){
if(!isset($result[$a['id']])){
$result[$a['id']]=array_merge($a,['occurrences'=>1]); // use id as temp key, preserve first found date
}else{
++$result[$a['id']]['occurrences']; // only update occurrences to preserve date
}
}
ksort($result); // optionally sort on id ASC
var_export(array_values($result)); // remove temporary keys from first level and print to screen
Output:
array (
0 =>
array (
'id' => '1',
'date' => '2016-03-11',
'occurrences' => 2,
),
1 =>
array (
'id' => '2',
'date' => '2016-04-22',
'occurrences' => 4,
),
)
You can do it like below:-
$final_array = array();
foreach($newarray as $arr){
if(!in_array($arr['id'],array_keys($final_array))){
$final_array[$arr['id']] = $arr;
$final_array[$arr['id']]['occurences'] = 1;
}else{
$final_array[$arr['id']]['occurences'] += 1;
}
}
$final_array= array_values($final_array);
print_r($final_array);
Output:- https://eval.in/847242
Note:- if you want final array to be ascending order of id then use usort() function like below:-
function cmpId($a, $b) {
return ($a['id'] - $b['id']);
}
usort($final_array, "cmpId");
print_r($final_array);
Output:- https://eval.in/847245

Sum of Dynamic Multi-Dimension array in PHP

I searched for few in-built methods and tried few logics from here and there but none can make me satisfied. I have created my own logic but is limited to a certain dimension of the array. I have managed to solve my problem for the project but as I said I am not satisfied.
Consider the array below:
$data = array
(
'0' => array
(
'0' => array
(
'make_ready' => '',
'ready_time' => '0.55',
'rate' => '46',
'no_of_run' => '',
'fixed_cost' => '',
'variable_cost' => '25.3',
'0' => array(
'kg' => 2.66,
'rate' => 11.4,
'fixed_cost' => '',
'variable_cost' => 30.32,
'0' => array(
'kg' => 2.66,
'rate' => 11.4,
'fixed_cost' => '',
'variable_cost' => 30.32,
),
),
),
),
'1' => array
(
'0' => array
(
'make_ready' => '1',
'ready_time' => '1.16',
'rate' => '36.47',
'no_of_run' => '',
'fixed_cost' => '36.47',
'variable_cost' => '42.31',
),
),
'2' => array
(
'make_ready' => '2',
'ready_time' => '0.29',
'rate' => '360',
'no_of_run' => '',
'fixed_cost' => '720',
'variable_cost' => '104.4',
),
'size' => '1000 X 1200 X 1190',
'up' => '3 X 4 / 17',
'unit' => '4',
'rate' => 16.32,
'fixed_cost' => '',
'variable_cost' => 65.28,
);
Problem:
I want to sum up all the values of array element where key is 'variable_cost', no matter how deep (in terms of dimension) is the array. Basically like a loop that can scan all the elements and do 'sum' if key matches.
I would like someone to help/suggest any logic where the calculations can be done till nth dimension.
For example like the movie Inception, in which they can go
dream->with in a dream->to nth number of dream.
and come out with the sum of 'variable_cost'. Hope you guys can understand the question here.
Thank you.
You will need to use a recursive function to sum all the variable_cost values:
function sum_variable_cost( $data, $total=0) {
foreach ($data as $key => $item) {
if (is_array($item)) {
$total = sum_variable_cost($item, $total);
} else if ($key=='variable_cost') {
//echo $key . " " . $item . "\n";
$total += $item;
}
}
return $total;
}
echo sum_variable_cost( $data );
Basically it loops through the array and sees if there are other arrays and only adds values that contain the key variable_cost.
If you uncomment the echo line it will show this output:
variable_cost 25.3
variable_cost 30.32
variable_cost 30.32
variable_cost 42.31
variable_cost 104.4
variable_cost 65.28
297.93

Removing selected elements from array of associative arrays

I have the following array of associative arrays.
$result = array(
(int) 0 => array(
'name' => 'Luke',
'id_number' => '1111',
'address' => '1544addr',
'time_here' => '2014-04-12 13:07:08'
),
(int) 1 => array(
'name' => 'Sam',
'id_number' => '2222',
'address' => '1584addr',
'time_here' => '2014-04-12 14:15:26'
I want to remove selected elements from this array such that it will look like this;
array(
(int) 0 => array(
'name' => 'Luke',
'id_number' => '1111'
),
(int) 1 => array(
'name' => 'Sam',
'id_number' => '2222',
This is the code I wrote;
foreach($result as $value)
{
unset($value('address') );
unset($value('time_here') );
}
When I run the code, Apache web server crashed.
Can the smarter members point out what did I do wrong? Thank you very much.
Array notation is wrong, use this;
$finalResult = array();
foreach($result as $value)
{
unset($value['address'] );
unset($value['time_here'] );
$finalResult[] = $value;
}
Here is a working demo: Demo
That is because you are not accessing array correctly. Use square bracket insted of round brackets :
foreach($result as $value)
{
unset($value['address'] );
unset($value['time_here'] );
}

php compare two multidimensional array and check for duplicate

I have two arrays
$array1 = array(
0 => array(
'user' => 'user0',
'id' => 'id0'
),
1 => array(
'user' => 'user1',
'id' => 'id1'
),
2 => array(
'user' => 'user2',
'id' => 'id2'
)
);
$array2 = array(
0 => array(
'emp' => 'emp0',
'id' => 'id3'
),
1 => array(
'emp' => 'emp1',
'id' => 'id1'
),
2 => array(
'emp' => 'emp2',
'id' => 'id2'
)
);
i need to loop array 2 first an d give input of id from array1 to the array 1 and search whether the value of id1 from arr1 exists in array2
Maybe this could work? (If I understood your question correctly)
$id_arr = array();
$final_arr = array();
checkArray($array1, $id_arr, $final_arr);
checkArray($array2, $id_arr, $final_arr);
function checkArray($arr, &$id_arr, &$final_arr) {
foreach ($arr as $key => $value) {
if (!in_array($value['id'], $id_arr)) {
$id_arr[] = $value['id'];
$final_arr[] = $value;
}
}
}
var_dump($final_arr);

Categories