I am trying to merge arrays with the same keys with different values, like below.
Input:
$array1 = array('1933' => array(
'nid' => '492811',
'title' => 'NEW TITLE',
'field_link_single_url' => 'abc',
'field_link_single_title' => 'test'
));
$array2 = array('1933' => array(
'nid' => '492811',
'title' => 'NEW TITLE',
'field_link_single_url' => 'xyz',
'field_link_single_title' => 'abcd'
));
Expected output to be:
Array
(
[nid] => 492811
[title] => NEW TITLE
[field_link_single_url] => [
[0] => 'abc',
[1] => 'xyz'
]
[field_link_single_title] => [
[0] => 'test',
[1] => 'abcd'
]
)
I have tried array_merge and array_merge_recursive but it does not work as expected.
Output of array_merge_recursive($array1[1933], $array2[1933]) it is creating duplicates keys having same value
Array
(
[nid] => Array
(
[0] => 492811
[1] => 492811
)
[title] => Array
(
[0] => Hal Louchheim
[1] => Hal Louchheim
)
[field_link_single_url] => Array
(
[0] => abc
[1] => xyz
)
[field_link_single_title] => Array
(
[0] => test
[1] => abcd
)
)
I made some assumption. If these assumptions are not valid, you should add some additional checks:
Both $array1 and $array2 are really arrays
Both $array1 and $array2 have the exact same keys
The values inside $array1 and $array2 are primitive types, not complex objects (the === operator would not work to compare objects or arrays)
function mergeArrayValues($array1, $array2) {
$output = [];
foreach($array1 as $key => $value) {
$output[$key] = $array1[$key] === $array2[$key]
? $array1[$key]
: [ $array1[$key], $array2[$key] ]
}
return $output;
}
mergeArrayValues($array1[1933], $array2[1933])
Related
I have 2 multidimensional arrays that I am working with:
$arr1 =
Array
([type] => characters
[version] => 5.6.7.8
[data] => Array
([Char1] => Array
([id] => 1
[name] =>Char1
[title] =>Example
[tags] => Array
([0] => DPS
[1] => Support))
[Char2] => Array
([id] => 2
[name] =>Char2
[title] =>Example
[tags] => Array
([0] => Tank
[1] => N/A)
)
)
etc...
$arr2=
Array
([games] => Array
([gameId] => 123
[gameType => Match
[char_id] => 1
[stats] => Array
([damage] => 55555
[kills] => 5)
)
([gameId] => 157
[gameType => Match
[char_id] => 2
[stats] => Array
([damage] => 12642
[kills] => 9)
)
etc...
Basically, I need almost all the data in $arr2... but only the Char name from $arr1. How could I merge or add the $arr1['name'] key=>value into $arr2 where $arr1['id'] is equal to $arr2['char_id'] as the "id" field of each array is the same number.
I've attempted using array_merge and array_replace, but I haven't come up with any working solutions. This is also all data that I am receiving from a 3rd party, so I have no control on initial array setup.
Thanks for any help or suggestions!
Actually, this is quite straighforward. (I don't think there a built-in function that does this.)
Loop $arr2 and under it loop also $arr1. While under loop, just add a condition that if both ID's match, add that particular name to $arr2. (And use some referencing & on $arr2)
Consider this example:
// your data
$arr1 = array(
'type' => 'characters',
'version' => '5.6.7.8',
'data' => array(
'Char1' => array(
'id' => 1,
'name' => 'Char1',
'title' => 'Example',
'tags' => array('DPS', 'Support'),
),
'Char2' => array(
'id' => 2,
'name' => 'Char2',
'title' => 'Example',
'tags' => array('Tank', 'N/A'),
),
),
);
$arr2 = array(
'games' => array(
array(
'gameId' => 123,
'gameType' => 'Match',
'char_id' => 1,
'stats' => array('damage' => 55555, 'kills' => 5),
),
array(
'gameId' => 157,
'gameType' => 'Match',
'char_id' => 2,
'stats' => array('damage' => 12642, 'kills' => 9),
),
),
);
foreach($arr2['games'] as &$value) {
$arr2_char_id = $value['char_id'];
// loop and check against the $arr1
foreach($arr1['data'] as $element) {
if($arr2_char_id == $element['id']) {
$value['name'] = $element['name'];
}
}
}
echo '<pre>';
print_r($arr2);
$arr2 should look now like this:
Array
(
[games] => Array
(
[0] => Array
(
[gameId] => 123
[gameType] => Match
[char_id] => 1
[stats] => Array
(
[damage] => 55555
[kills] => 5
)
[name] => Char1 // <-- name
)
[1] => Array
(
[gameId] => 157
[gameType] => Match
[char_id] => 2
[stats] => Array
(
[damage] => 12642
[kills] => 9
)
[name] => Char2 // <-- name
)
)
)
Iterate over $arr2 and add the data to it from the matching $arr1 array value:
$i = 0;
foreach($arr2['games'] as $arr2Game){
$id = $arr2Game['char_id'];
$arr2['games'][$i]['name'] = $arr1['data'][$id]['name'];
$i++;
}
Have not tested this code.
If I'm understanding you correctly, you want to add a name index to each of the arrays within the $arr2['games'] array.
foreach($arr2['games'] as $key => $innerArray)
{
$arr2['games'][$key]['name'] = $arr1['data']['Char'.$innerArray['char_id']]['name'];
}
I am trying to create filtered array 3 using array 1 and array 2.
ARRAY 1
Array (
[title] => value
[title2] => value2
[title3] => value3
)
ARRAY 2
Array (
[0] => Array ( [id] => 20 [title2] => value2 [otherColumn1] => otherValue1)
[1] => Array ( [id] => 21 [title4] => value4 [otherColumn3] => otherValue3)
)
Desired Result after applying the intersection method:
ARRAY 3
Array ( [title2] => value2 )
So far I am unable to achieve the result because array 1 and 2 have non-matching structures. I have tried different techniques but I am unable compare them due to structure differences.
if (!empty($data1['array2'][0])) {
foreach ($data1['array2'] as $key) {
// $filtered= array_intersect($array1,$key);
// print_r($key);
}
// $filtered= array_intersect($array1,$data1['array2']);// if i use $data1['array2'][0] it filters fine but just one row
// print_r($filtered);
}
Any help would be much appreciated. Thank you.
Given the arrays:
$arr = array('title' => 'value', 'title2' => 'value2', 'title3' => 'value3');
$arr2 = array (
0 => array ( 'id' => '20', 'title2' => 'value2', 'otherColumn1' => 'otherValue1'),
1 => array ( 'id' => '21', 'title4' => 'value4', 'otherColumn3' => 'otherValue3'));
You can get your filtered array with this:
$merged = call_user_func_array('array_merge', $arr2);
$filtered = array_intersect($arr, $merged);
If you want to intersect just according to the keys you can use this instead:
$filtered = array_intersect_key($arr, $merged);
you can remove structural difference in the following way
$arr1 = array (
0 => array('title' => 'value', 'title2' => 'value2', 'title3' => 'value3'));
$arr2 = array (
0 => array ( 'id' => '20', 'title2' => 'value2', 'otherColumn1' => 'otherValue1'),
1 => array ( 'id' => '21', 'title4' => 'value4', 'otherColumn3' => 'otherValue3'));
I have the following 2 arrays that I'm trying to merge recursively so that I don't lose data, but I also don't want any data repeated.
$a = array(
'group1' => array(
'names' => array(
'g1name1',
'g1name2'
)
),
'group2' => array(
'names' => array(
'g2name1'
)
)
);
$b = array(
'group1' => array(
'names' => array(
'g1name1',
'g1name3'
),
'extras' => array(
'g1extra1'
)
),
'group3' => array(
'names' => array(
'g3name1'
)
)
);
I'm using array_merge_recursive($a, $b); which works fine for group2 and group3 because they exist in either $a or $b, but group1 is giving me a problem because it has some duplicate data in both $a and $b. This is what I'm getting after I merge recursively. Notice that in names, g1name is listed twice, once from $a and once from $b.
'group1' =>
array
'names' =>
array
0 => string 'g1name1'
1 => string 'g1name2'
2 => string 'g1name1'
3 => string 'g1name3'
'extras' =>
array
0 => string 'g1extra1'
1 => string 'g1extra2'
'group2' => ....
'group3' => ....
I tried array_unique like this array_unique(array_merge_recursive($a, $b)) but it's giving me strange results (doesn't fix the repeat problem and deletes group2 and group3 entirely).
Use array_replace_recursive instead of array_merge_recursive.
The the following should work for you:
array_walk($arr, function(&$data, $key) {
foreach ($data as &$arr) {
$arr = array_values(array_unique($arr));
}
});
Result:
Array
(
[group1] => Array
(
[names] => Array
(
[0] => g1name1
[1] => g1name2
[2] => g1name3
)
[extras] => Array
(
[0] => g1extra1
)
)
[group2] => Array
(
[names] => Array
(
[0] => g2name1
)
)
[group3] => Array
(
[names] => Array
(
[0] => g3name1
)
)
)
Sadly, array_replace_recursive (PHP 5.3.0)Docs does not work (Demo).
Luckily Tim Cooper pointed that out and made some suggestion. I was not very creative with my new suggestion, so it's highly influenced by his solution:
$result = array_merge_recursive($a, $b);
array_walk($result, function(&$v) {
$v = array_map('array_unique', $v);
});
New Demo
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.
I have two arrays:
Array
(
[0] => Array
(
[id] => 1
[type] => field
[remote_name] => Title
[my_name] => title
[default_value] => http%3A%2F%2Ftest.com
)
[1] => Array
(
[id] => 2
[type] => field
[remote_name] => BookType
[my_name] => book-type
[default_value] =>
)
[2] => Array
(
[id] => 3
[type] => value
[remote_name] => dvd-disc
[my_name] => dvd
[default_value] =>
)
)
Array
(
[title] => Test
[book-type] => dvd
)
I need to take each key in the second array, match it with the my_name value in the first array and replace it with the corresponding remote_name value of the first array while preserving the value of the second array.
There's got to be some carrayzy function to help!
EDIT: There will also be a few cases that the value of the second array will need to be replaced by the value of the first array's remote_name where the value of the second array matches the value of the first array's my_name. How can I achieve this?
EG: book-type => dvd should turn into BookType => dvd-disc
Like so?:
$first = array(
array(
'id' => 1,
'type' => 'field',
'remote_name' => 'Title',
'my_name' => 'title',
'default_value' => 'http%3A%2F%2Ftest.com',
),
array(
'id' => 2,
'type' => 'field',
'remote_name' => 'BookType',
'my_name' => 'book-type',
'default_value' => '',
),
array(
'id' => 3,
'type' => 'value',
'remote_name' => 'dvd-disc',
'my_name' => 'dvd',
'default_value' => '',
),
);
$second = array(
'title' => 'Test',
'book-type' => 'dvd',
);
$map = array('fields' => array(), 'values' => array());
foreach ($first as $entry) {
switch ($entry['type']) {
case 'field':
$map['fields'][$entry['my_name']] = $entry['remote_name'];
break;
case 'value':
$map['values'][$entry['my_name']] = $entry['remote_name'];
break;
}
}
$new = array();
foreach ($second as $key => $val) {
$new[isset($map['fields'][$key]) ? $map['fields'][$key] : $key] = isset($map['values'][$val]) ? $map['values'][$val] : $val;
}
print_r($new);
Output:
Array
(
[Title] => Test
[BookType] => dvd-disc
)
Explanation:
The first loop collects the my_name/remote_name pairs for fields and values and makes them more accessible.
Like so:
Array
(
[fields] => Array
(
[title] => Title
[book-type] => BookType
)
[values] => Array
(
[dvd] => dvd-disc
)
)
The second loop will traverse $second and use the key/value pairs therein to populate $new. But while doing so will check for key/value duplicates in $map.
Keys or values not found in the map will be used as is.
foreach($arr1 as &$el) {
$el['remote_name'] = $arr2[$el['my_name']];
}
unset($el);
I am not aware of such a carrayzy function, but I know how you could do it:
//$array1 is first array, $array2 is second array
foreach($array1 as $key => $value){
if (isset($value['remote_name'], $value['my_name']) && $value['remote_name'] && $value['my_name']){
$my_name = $value['my_name'];
if (isset($array2[$my_name])) {
$remote_name = $value['remote_name'];
$array2[$remote_name] = $array2[$my_name];
//cleanup
unset($array2[$my_name]);
}
}
}