Merge 2d arrays with same index value - php

How can I merge one array inside another 2d array based on a unique value that is shared on both 2d array?
Arrays:
$arr1 = [
"First" =>[
"Name" => "John",
"Id" => 123
],
"Second" =>[
"Name" => "Peter",
"Id" => 45
]
];
$arr2 = [
"First" =>[
"Age" => 34,
"Id" => 123
],
"Second" =>[
"Age" => 24,
"Id" => 45
]
];
$n = array_merge($arr1, $arr2);
Current output when var_dump $n:
array(2) {
["First"]=>
array(2) {
["Age"]=>
int(34)
["Id"]=>
int(123)
}
["Second"]=>
array(2) {
["Age"]=>
int(24)
["Id"]=>
int(45)
}
}
Desired output:
array(2) {
["First"]=>
array(2) {
["Name"]=>
String("John")
["Age"]=>
int(34)
["Id"]=>
int(123)
}
["Second"]=>
array(2) {
["Name"]=>
String("Peter")
["Age"]=>
int(24)
["Id"]=>
int(45)
}
}
Ofcourse just by merging the two arrays wont fix my issue, but i was just wondering what approach I should take to do this.

Assuming this structure of your arrays, you can use array_replace_recursive.
$n = array_replace_recursive($arr1, $arr2);
Fiddle: https://3v4l.org/IPGsl
Some people can say that you can use array_merge_recursive, but no. As you have same key (Id) in both arrays, resulting array will have not the structure you expect. But in case you have different keys in both arrays - array_merge_recursive is an option too.

Related

PHP: How to flatten a multidimensional array by duplicate values

I have the following array:
array(3) {
[0]=>
array(2) {
["label"]=> string(10) "Chardonnay"
["value"]=> int(245)
}
[1]=>
array(2) {
["label"]=> string(10) "Chardonnay"
["value"]=> int(33)
}
[2]=>
array(2) {
["label"]=> string(10) "Chardonnay"
["value"]=>int(175)
}
[3]=>
array(2) {
["label"]=> string(10) "Stein"
["value"]=>int(195)
}
}
How would I go about "filtering" this array so that it looks like this:
array(2) {
[0]=>
array(2) {
["label"]=> string(5) "Chardonnay"
["value"]=> int(245)
}
[1]=>
array(2) {
["label"]=> string(10) "Stein"
["value"]=>int(195)
}
}
So in essence, I want to remove array elements by value where the value is not unique.
I don't even know where to start. Please assist?
Since keys must be unique, you can index on the label and you will only have one of each:
$result = array_column($array, null, 'label');
If you just want a single dimension with unique values, extract all labels and then make it unique:
$result = array_unique(array_column($array, 'label'));
If this array is coming from a database query then you would do it in the query instead.
You can use this code:
<?php
$arr = [
[
"label"=> "Chardonnay",
"value"=> 245,
],
[
"label"=> "Chardonnay",
"value"=> 33,
],
[
"label"=> "Chardonnay",
"value"=> 75,
],
[
"label"=> "Stein",
"value"=> 195,
],
];
$arr = array_intersect_key($arr,array_unique(array_column($arr,'label')));
print_r($arr);
It will remove duplicates, then intersect the keys (0,3 in this case) with the original array. An alternative to preserve the original keys.

PHP indexed array to nested associative array

I need to convert simple array to nested array according to specific rules. I've achived it but I'm looking for better solution.
SIMPLE:
array(4) {
[0]=>
array(2) {
["id"]=>
string(2) "11"
["type"]=>
int(3)
}
[1]=>
array(2) {
["id"]=>
string(2) "10"
["type"]=>
int(2)
}
[2]=>
array(2) {
["id"]=>
string(1) "1"
["type"]=>
int(1)
}
[3]=>
array(2) {
["id"]=>
string(1) "0"
["type"]=>
int(1)
}
}
EXPECTED EFFECT:
array(1) {
[0]=>
array(2) {
["type"]=>
int(1)
["child"]=>
array(1) {
[1]=>
array(2) {
["type"]=>
int(1)
["child"]=>
array(1) {
[10]=>
array(2) {
["type"]=>
int(2)
["child"]=>
array(1) {
[11]=>
array(2) {
["type"]=>
int(3)
["child"]=>
array(0) {
}
}
}
}
}
}
}
}
}
MY SOLUTION (not very satisfying):
$nestedArray = [];
foreach ($simpleArray as $item)
{
if (!empty($nestedArray))
{
$array = $nestedArray;
reset($array);
$firstKey = key($array);
}
$nestedArray[$item['id']]['child'] = $nestedArray;
$nestedArray[$item['id']]['type'] = $item['type'];
if (!empty($firstKey))
{
unset($nestedArray[$firstKey]);
}
}
As I said, I'm looking for more elegant way to achieve that. Rule are very simply: every next item is child of previous.
You could use recursion:
function nest($arr) {
return count($arr) ? ["type" => array_pop($arr)["type"], "child" => nest($arr)] : [];
}
With your example input, it would look like this:
$simpleArray = [
["id" => "11", "type" => 3],
["id" => "10", "type" => 2],
["id" => "1", "type" => 1],
["id" => "0", "type" => 1]
];
function nest($arr) {
return count($arr) ? ["type" => array_pop($arr)["type"], "child" => nest($arr)] : [];
}
$nested = nest($simpleArray));
$nested will have the following value:
[
"type" => 1,
"child" => [
"type" => 1,
"child" => [
"type" => 2,
"child" => [
"type" => 3,
"child" => []
]
]
]
]

Restructure array data by chunking, transposing, and merging

I have array mentioned below, I will have value always multiple of 3.
$xyz = [
["name" => "abc"],
["name" => "snds"],
["name" => ""),
["number"=> "452"],
["number" => "845120"],
["number" => "84514513200"],
["email" => "ddddf"],
["email" => "dkskns"],
["email" => "kjnksdnkds"]
];
but this is not the proper format for me to perform further operations, so I want this array like mentioned below.
$abc = [
[
"name" => "abc",
"number" => '452',
"email" => "ddddf"
],
[
"name" => "snds",
"number" => "845120",
"email" => "dkskns"
],
[
"name" => "",
"number" => "84514513200",
"email" => "kjnksdnkds"
]
];
note: the array length is dynamic but it will always be multiple of 3
One possibility could be to use the modulo % operator.
In the foreach the value is an array and you could use array_keys to get the key and reset to get the value of the first array element.
$result = [];
$count = 0;
foreach ($xyz as $value) {
if ($count%3 === 0) {
$count = 0;
}
$result[$count][array_keys($value)[0]] = reset($value);
$count++;
}
Demo
That will give you:
array(3) {
[0]=>
array(3) {
["name"]=>
string(3) "abc"
["number"]=>
string(3) "452"
["email"]=>
string(5) "ddddf"
}
[1]=>
array(3) {
["name"]=>
string(4) "snds"
["number"]=>
string(6) "845120"
["email"]=>
string(6) "dkskns"
}
[2]=>
array(3) {
["name"]=>
string(0) ""
["number"]=>
string(11) "84514513200"
["email"]=>
string(10) "kjnksdnkds"
}
}
This will do:
$result = array_map('array_merge', ...array_chunk($xyz, count($xyz) / 3));

Grouping array items based on string value

I am trying to group an array by sport. It could be n number of sports. Finally, then create a new array with it. Is there an efficient way to this without going overkill?
$sports = [
['sport' => 'soccer', 'id' => 97487];
['sport' => 'soccer', 'id' => 244800];
['sport' => 'soccer', 'id' => 258740];
['sport' => 'basketball', 'id' => 147884];
['sport' => 'baseball', 'id' => 222240];
['sport' => 'baseball', 'id' => 222245];
];
Initial array:
array(6) {
[0]=>
array(2) {
["sport"]=>
string(6) "soccer"
["id"]=>
int(97487)
}
[1]=>
array(2) {
["sport"]=>
string(6) "soccer"
["id"]=>
int(244800)
}
[2]=>
array(2) {
["sport"]=>
string(6) "soccer"
["id"]=>
int(258740)
}
[3]=>
array(2) {
["sport"]=>
string(10) "basketball"
["id"]=>
int(147884)
}
[4]=>
array(2) {
["sport"]=>
string(8) "baseball"
["id"]=>
int(222240)
}
[5]=>
array(2) {
["sport"]=>
string(8) "baseball"
["id"]=>
int(222245)
}
}
Desired results:
array(3)
{
[0]=>
array(3) {
[0]=>
array(2) {
["sport"]=>
string(6) "soccer"
["id"]=>
int(97487)
}
[1]=>
array(2) {
["sport"]=>
string(6) "soccer"
["id"]=>
int(244800)
}
[2]=>
array(2) {
["sport"]=>
string(6) "soccer"
["id"]=>
int(258740)
}
}
[1]=>
array(1) {
[0]=>
array(2) {
["sport"]=>
string(10) "basketball"
["id"]=>
int(147884)
}
}
[2]=>
array(2) {
[0]=>
array(2) {
["sport"]=>
string(8) "baseball"
["id"]=>
int(222240)
}
[1]=>
array(2) {
["sport"]=>
string(8) "baseball"
["id"]=>
int(222245)
}
}
}
You can group the array like so:
$sports = [
['sport' => 'soccer', 'id' => 97487],
['sport' => 'soccer', 'id' => 244800],
['sport' => 'soccer', 'id' => 258740],
['sport' => 'basketball', 'id' => 147884],
['sport' => 'baseball', 'id' => 222240],
['sport' => 'baseball', 'id' => 222245]
];
// we will build an array here where the key is the sport
// name and the value is an array of objects pertaining
// to that sport i.e. 'basketball' => [bb1, bb2, ...]
$array = array();
// now consider every sport object in your original array
foreach($sports as $key => $item)
{
if (array_key_exists($item['sport'], $array)) {
// we encountered this same sport in the past so we
// know $array['sportName'] already exists and can
// push right to it
$array[$item['sport']][] = $item;
} else {
// we have never seen this sport before and now must
// insert the sport into $array['sportName'] = []
// and push this sport object to it
$array[$item['sport']] = [$item];
}
}
// since $array's keys are the names of the sports themselves, but
// you want the keys to be numeric, this will build a new array
// from just the values of $array which at this point contains
// grouped arrays of sport objects
$result = array_values($array);
// print the results for good measure :)
print_r($result);
This works by looping over your sports array and building a second array of [sportName => arrayOfThatSport]. Inside the for loop, we are checking to see if a given sport already exists in this array. If it does, great, add the sport to the corresponding array for that sport. Otherwise, create a new array for that sport available at $array['sportName'] = [sportObject]. If you consider several iterations of the loop you will see that we're either always adding to an existing $array['sportName'] array, or creating a new array at this position whenever we encounter a new sport we've never seen before. This gives us a final array similar to: [ "sport
: [...], "sport2": [...] ] but in your case you want a numeric array not an associative array, hence the call to array_values.
This is my suggested solution.
$groupedSports = [];
foreach ($sports as $item) {
if (empty($groupedSports[$item['sport']])) {
$groupedSports[$item['sport']] = [];
}
$groupedSports[$item['sport']][] = $item;
}
Short explanation:
line 1: we initialize an empty array
line 2: we start a loop on the original array
line 3-5: if it's the first occurrence for the current sport, we initialize an empty array for that sport (avoid warnings)
line 6: we assign current item to the appropriate array

Natural sort an array of rows based on values in one column

I have this multidimensional array:
$data[] = array('name' => 'Mini 16', 'id' => 105);
$data[] = array('name' => 'Mini 15', 'id' => 5650);
$data[] = array('name' => 'Mini 100', 'id' => 9889);
$data[] = array('name' => 'Mini 20', 'id' => 587);
I want to order the array by name column sorting naturally, but is difficult for me.
The expected result:
[
['name' => 'Mini 15', 'id' => 5650],
['name' => 'Mini 16', 'id' => 105],
['name' => 'Mini 20', 'id' => 587],
['name' => 'Mini 100', 'id' => 9889]
]
You can use usort() to sort the array by a custom function, and use strnatcmp() to do the natural comparison of two strings like so:
usort( $data, function( $el1, $el2) { return strnatcmp( $el1['name'], $el2['name']); });
So before, your array was this:
array(4) {
[0]=>
array(2) {
["name"]=>
string(7) "Mini 16"
["id"]=>
int(105)
}
[1]=>
array(2) {
["name"]=>
string(7) "Mini 15"
["id"]=>
int(5650)
}
[2]=>
array(2) {
["name"]=>
string(8) "Mini 100"
["id"]=>
int(9889)
}
[3]=>
array(2) {
["name"]=>
string(7) "Mini 20"
["id"]=>
int(587)
}
}
And now it looks like:
array(4) {
[0]=>
array(2) {
["name"]=>
string(7) "Mini 15"
["id"]=>
int(5650)
}
[1]=>
array(2) {
["name"]=>
string(7) "Mini 16"
["id"]=>
int(105)
}
[2]=>
array(2) {
["name"]=>
string(7) "Mini 20"
["id"]=>
int(587)
}
[3]=>
array(2) {
["name"]=>
string(8) "Mini 100"
["id"]=>
int(9889)
}
}
Note that for lower versions of PHP, you won't be able to use an anonymous function, and would instead need something like this:
usort( $data, create_function( '$el1, $el2', 'return strnatcmp( $el1[\'name\'], $el2[\'name\']);' ));
If you're using PHP 5.4 or newer, you can use array_multisort with the SORT_NATURAL flag. Just follow Example #3 in the http://php.net/manual/en/function.array-multisort.php documentation, but add the SORT_NATURAL option.
As half-answered by Craig, array_multisort() will be more performant than usort() because array_multisort() doesn't make iterated function calls.
Code: (Demo)
array_multisort(array_column($data, 'name'), SORT_NATURAL, $data);
var_export($data);
This is the modern, arrow-function syntax for nickb's answer.
Code (PHP7.4 and higher): (Demo)
usort($data, fn($a, $b) => strnatcmp($a['name'], $b['name']));
var_export($data);

Categories