PHP merge arrays if they have the same key value - php

I have an array of associative arrays.
$array = [
['report_date' => 'date', 'name' => 'name'],
['report_date' => 'date', 'color' => 'color']
];
I want to sort through this array and if
$array[x]['report_date'] === $array[y]['report_date']
then I need to perform a merge that would return in this case:
$newArray = [['report_date'=>date,'name'=>name,'color'=>color]]
Of course we would need to take into account that there may be multiple arrays that fulfill this requirement and we would have to merge them as well.
I've tried a couple of things, that resulted in mapping and foreach merges that took forever to process and in the end couldn't get it working.
Any ideas?

You can create a new array, indexed by report_date and push the values into it:
$out=[];
foreach($array as $subarray){
foreach($subarray as $key=>$val){
$out[$subarray['report_date']][$key]=$val;
}
}
var_dump($out);

Related

Given array keys that correspond to another array, how can I merge them?

I have the following array that is supposed to only be keys:
$keys = ['mod_4_key'];
and the bigger array which contains a lot of information:
$big_array = [ 'mod_4_key' => ['old' => '', 'info' => ''], 'mod_5_key' => ..]
I would like to, based on what is inside $keys generate a new array with the information from $big_array, as such, if we are to compute the "non-difference" between the arrays, the output should be:
$final_array = [ 'mod_4_key' => ['old' => '', 'info' => '']]
I achieved this using a classic foreach but I was wondering if there was no in-built way to achieve this.
You may be better off with a simple foreach() loop, but there are probably several ways of achieving this.
This uses array_flip() on the $keys, so that you end up with another associative array, then use array_intersect_key() with the big array first.
$final_array = array_intersect_key($big_array, array_flip($keys));

array_diff_assoc() or foreach()? Which is faster?

I have two arrays, for example $session and $post with 100+ values. I will compare the $post array values with $session array. If post is different then it will be taken to result array else not.
We can try this using array_diff_assoc($post, $session) and foreach(). Which one is faster?
For profiling, Phil has suggested a great way in his reply, but I will link it here too, just in case:
Simplest way to profile a PHP script
Practically, you need to know what each approach does. in array_diff_assoc, you are returning the difference between 2 collections, after comparing the key/value couples for each element. It will then return an array that contains the entries from array1 that are not present in array2 or array3, etc.
In a for each loop, you will need to hard code the same function (assuming that's what you need). You will need to take the first element, then look for the combination in your other arrays. If it matches your requirements, you will save it into your output array, or even print it directly.
Same principles apply, but then again, it will be up to profiling to determine the faster approach. Try doing so on a large number of big arrays, as the difference isn't noticeable at smaller scales.
I'll leave this as a stub/example, please edit, or use for profiling.
<?php
$before = [
'name' => 'Bertie',
'age' => '23'
];
$after = [
'name' => 'Harold',
'age' => '23',
'occupation' => 'Bus driver'
];
function changed_1($after, $before) {
return array_diff_assoc($after, $before);
}
function changed_2($after, $before) {
$changed = [];
foreach($after as $k => $v) {
if(isset($before[$k]) && $before[$k] !== $v)
$changed[$k] = $v;
if(!isset($before[$k]))
$changed[$k] = $v;
}
return $changed;
}
var_export(changed_1($after, $before));
var_export(changed_2($after, $before));
Output:
array (
'name' => 'Harold',
'occupation' => 'Bus driver',
)array (
'name' => 'Harold',
'occupation' => 'Bus driver',
)

Working with multidimensional array with PHP

I have this array:
$datas = array(
array(
'id' => '1',
'country' => 'Canada',
'cities' => array(
array(
'city' => 'Montreal',
'lang' => 'french'
),
array(
'city' => 'Ottawa',
'lang' => 'english'
)
)
)
);
Question 1:
How can I get the the country name when I have the id ?
I tried: $datas['id'][1] => 'country'
Question 2:
How can I loop in the cities when I have the id ?
I tried:
foreach ($datas as $data => $info) {
foreach ($info['cities'] as $item) {
echo '<li>'.$item['city'].'</li>';
}
}
Thanks a lot.
You have the ID of the array you want analyse, but your array is structured as a map, meaning that there are no keys in the outer array. You will therefore have to iterate the array first to find the object you are looking for.
While the first approach would be to search for the object that has the ID you are looking for, i suggest you map your arrays with their IDs. To do that, you can use two PHP array functions: array_column and array_combine.
array_column can extract a specific field of each element in an array. Since you have multiple country objects, we want to extract the ID from it to later use it as a key.
array_combine takes two arrays with the same size to create a new associative array. The values of the first array will then be used as keys, while the ones of the second array will be used as values.
$mappedCountries = array_combine(array_column($datas, 'id'), $datas);
Assuming that the key 1 is stored in the variable $key = 1;, you can afterwards use $mappedCountries[$key]['country'] to get the name of the country and $mappedCountries[$key]['cities'] to get the cities, over which you can then iterate.
if there might be many arrays in $datas and you want to find one by id (or some other key) you can do something like this:
function search($datas, $key, $value) {
foreach($datas as $data) {
if ($data[$key] === $value) {
return $data;
}
}
So if you want to find where id = 1
$result = search($datas, 'id', '1');
and then you can get country echo $result['country'] or whatever you need.

Building a new array using key IDS

Using the numbers from $ids, I want to pull the data from $nuts.
So for example:
$ids = [0,3,5]; // 0 calories, 3 sugar, 5 fat
$nuts = [
'calories' => 'cal',
'protein' => 'pro',
'carbohydrate' => 'car',
'sugar' => 'sug',
'fiber' => 'fib',
'fat' => 'fat',
];
$returnData = [
'calories' => 'cal',
'sugar' => 'sug',
'fat' => 'fat',
];
I could loop through each $ids number with a foreach(); but I'm curious to see if there is a better method than this?
$newNuts = array_values(array_flip($nuts));
foreach($ids as $i)
$returnData[$newNuts[$i]] = $nuts[$newNuts[$i]];
I did some work and realized, you don't need array_flip, array_values is fine.
$num_nuts = array_values ($nuts);
for ($z=0; $z<sizeof($ids); $z++) {
echo $num_nuts[$ids[$z]];
}
Just 1 more line of code, but I think it does the job. I think mine is going to be faster because the array_flip basically exchanges all keys with their associated values in an array, which is not what I am doing. It's actually one less pain.
I am simply converting the original array to a new one by index and simply looping upon it. Also, not the elegant way to use the power of PHP available to us, but works just fine. array_flip is O(n), but I think better not use it for larger data-sets.
How about a simple array_slice?
$result = array();
foreach ($ids as $i) {
$result += array_slice($nuts, $i, 1, true);
}
No need to create a copy of the array.

Merge row data from multiple arrays

I have two arrays as shown below. I need to merge the content of the arrays so that I can get the structure as shown in the third array at last. I have checked array_merge but can't figure out the way this is possible. Any help appreciated. Thanks.
[
['gross_value' => '100', 'quantity' => '1'],
['gross_value' => '200', 'quantity' => '1']
]
and
[
['item_title_id' => '1', 'order_id' => '4'],
['item_title_id' => '2', 'order_id' => '4']
];
I should get a merged array like this:
[
[
'gross_value' => '100',
'quantity' => '1',
'item_title_id' => '1',
'order_id' => 4
],
[
'gross_value' => '200',
'quantity' => '1',
'item_title_id' => '2',
'order_id' => 4
]
]
Use array_merge_recursive :
Convert all numeric key to strings, (make is associative array)
$result = array_merge_recursive($ar1, $ar2);
print_r($result);
See live demo here
how about:
$arr1 = array(
0 => array(
'gross_value' => '100',
'quantity' => '1'
),
1 => array(
'gross_value' => '200',
'quantity' => '1'
)
);
$arr2 = array(
0 => array(
'item_title_id' => '1',
'order_id' => '4'
),
1 => array(
'item_title_id' => '2',
'order_id' => '4'
)
);
$arr = array();
foreach($arr1 as $k => $v) {
array_push($arr, array_merge($v, $arr2[$k]));
}
print_r($arr);
output:
Array
(
[0] => Array
(
[gross_value] => 100
[quantity] => 1
[item_title_id] => 1
[order_id] => 4
)
[1] => Array
(
[gross_value] => 200
[quantity] => 1
[item_title_id] => 2
[order_id] => 4
)
)
Have a look at array_merge
I would probably iterate over the arrays and merge them manually.
$result = array();
foreach ( $array1 as $key => $item )
{
$result[$key] = array_merge($array1[$key], $array2[$key]);
}
You will have an issue if the top-level arrays don't have strictly matching keys though.
If you have $array1 and $array2, try this:
foreach($array1 as $key1=>$innerArray){
$array1[$key1]['item_title_id'] = $array2[$key1]['item_title_id'];
$array1[$key1]['order_id'] = $array2[$key1]['order_id'];
}
The problem with things like merge recursive is that they don't know when to stop.
In some scenarios you want to stop traversing down an array and simply take a given value if it exists.
For instance if you have to override a nested config array you might not want the default keys to stick around at a a specific level.
here is my solution:
public static function merge_lvl2(){
$args = func_get_args();
return static::merge($args, 2);
}
public static function merge($args, $maxDepth = null, $depth = 1)
{
$merge = [];
foreach($args as $arg) {
if (is_array($arg)) {
if (is_array($merge)) {
if ($maxDepth == $depth) {
$arg += $merge;
$merge = $arg;
} else {
$merge = array_merge($merge, $arg);
}
} else {
$merge = $arg;
}
}
}
if ($maxDepth !== $depth) {
foreach($args as $a) {
if (is_array($a)) {
foreach($a as $k => $v) {
if (isset($merge[$k]) && is_array($merge[$k])) {
$merge[$k] = static::merge([$merge[$k], $v], $maxDepth, $depth + 1);
}
}
}
}
}
return $merge;
}
You can pass as many arrays to merge as you want to.
$merged = ClassName::merge_lvl2([..array1..], [..array2..], [..array3..], etc...);
It will stop merging at level 2 and accept the last instance of the key as an override instead of a merge.
You can also call merge directly with an array of args and setting the max depth.
If no max depth is set it will traverse the entire array.
The most modern, elegant, concise way to merge rows from two or more arrays (or the rows from a multidimensional array with 3 or more levels of depth) is to call array_merge() on each row (array_replace() can also be used). array_map() can call array_merge by its string name and the input data can be split into individual arguments with the "spread operator" (...) when needed.
Code for the OP's arrays: (Demo)
var_export(
array_map('array_merge', $arr1, $arr2)
);
The above technique will return a newly indexed array (though you might not notice because the sample input arrays were indexed to begin with). If your input data has associative first-level keys, they will be ignored and destroyed by this technique. If you have non-numeric first-level keys and want to merge on those, then array_merge_recursive() is likely to be the ideal native function - Demo.
However, it must be said, that for the OP's sample data array_merge_recursive() IS NOT a correct technique.
My first snippet is conveniently extended if you have more than two arrays which need their rows to be merge based on their positions. (Demo)
var_export(
array_map('array_merge', $arr1, $arr2, $arr3)
);
And as mentioned earlier, the spread operator can be used to unpack deeper arrays with the same result. Again, the number of subarrays containing rows can be dynamic. If your deep array only has one subarray containing rows, then the result will be a "flattening" effect where the top level is removed.
Code with new data structure: (Demo)
$masterArray = [
[
['gross_value' => '100', 'quantity' => '5'],
['gross_value' => '200', 'quantity' => '6']
],
[
['item_title_id' => '1', 'order_id' => '3'],
['item_title_id' => '2', 'order_id' => '4']
],
[
['foo' => 'bar1'],
['foo' => 'bar2']
]
];
var_export(
array_map('array_merge', ...$masterArray)
);
To be fair, array_replace_recursive() does provide the desired result using the OP's sample data, but I find the technique to be semantically misleading. (Demo)
All that said, you are not forced to use PHP's native array functions; you can use classic loops as well -- you will have several ways to "unite" the rows inside the loop. This approach is a little less D.R.Y. because you need to explicitly specific the separate arrays that you wish to synchronously iterate. Just make sure that you understand the nuanced differences in using array_merge(), array_replace(), and the union operator (+) with different qualities of data. Hint: associative, non-numeric keyed data in the respective rows will be affected when key collisions occur. Be careful to use a merging technique that will not overwrite the wrong data when rows share associative keys.
array_merge() to build a new array: (array_merge() Demo) (array_replace() Demo) (array union operator + Demo)
$result = [];
foreach ($arr1 as $i => $row) {
$result[] = array_merge($row, $arr2[$i]);
}
var_export($result);
Alternative, you can use the same general approach, but instead of populating a new $result array, you can merge data into the first array.
(array_merge() Demo) (array_replace() Demo) (array union assignment operator += Demo)
foreach ($arr1 as $i => &$row) {
$row = array_merge($row, $arr2[$i]);
}
var_export($arr1);
For the sake of completeness, if you have an indexed array of indexed arrays, then you might even use iterated calls of array_push() with the spread operator to achieve a similar functionality. This quite literally, indicatively appends the row data from subsequent arrays to the first array's rows.
Code: (Demo)
$arr1 = [
['A', 'B', 'C'],
['F', 'G']
];
$arr2 = [
['D', 'E'],
['H', 'I', 'J', 'L']
];
foreach ($arr1 as $i => &$row) {
array_push($row, ...$arr2[$i]);
}
var_export($arr1);
Related non-duplicate content on Stack Overflow:
Partially merge one array's row data with another another array:Add column of values from one array to another
Merge two flat arrays to create an array of merged rows:Transforming array values in elements of a subarray using PHP
Merge rows with indexed elements, remove duplicates and reindex:Merge two multidimensional arrays, preserve numeric keys, and combine values inside array
Merge arrays containing objects:Merge rows of two arrays containing objects by first level index
Push single elements from one array to rows in another array:Push elements from one array into rows of another array (one element per row)
On the above pages, the rabbit hole goes further because I've linked other related pages to them. Keep researching until you find what you need.
If you are using Laravel, you might be interested in its combine() and collect() methods.

Categories