Merge two multidimensional arrays using recursion, but not on full data set - php

I'm trying to add 2 array data to each other with array_merge(). It's just attached to the back. But lower levels are ignored.
Is there an alternative to array_merge() that will merge the user values without duolicating the color values?
Existing array data:
$existingtArr = [
"A" => [
"color" => 'red',
"user" => [
"Daniel" => ["01:18:08", "04:10:12"],
"Max" => ["01:04:00"],
"Serto" => ["02:00:02"],
]
],
"B" => [
"color" => 'blue',
"user" => [
"Franz" => ["08:40:52"],
"Hugo" => ["07:08:58"],
]
]
];
New array data:
$newArr = [
"A" => [
"color" => 'red',
"user" => [
"Fabian" => ["06:03:00"], // + 1 user
"Max" => ["04:10:12"], // + 1 new time
"Serto" => ["02:00:02"],
]
],
"B" => [
"color" => 'blue',
"user" => [
"Franz" => ["08:40:52", "14:05:32", "20:34:15"], // an older one is available, + 2 new times
"Hugo" => ["04:10:12"], // + 1 time
]
],
"C" => [ // + new whole group
"color" => 'green',
"user" => [
"Maxi" => ["07:08:58", "04:10:12"],
]
]
];
Supplement the existing data with the new data:
echo '<pre>';
print_r(array_merge($existingtArr, $newArr));
echo '</pre>';
Expected result array data:
$resultArr = [
"A" => [
"color" => 'red',
"user" => [
"Daniel" => ["01:18:08", "04:10:12"],
"Fabian" => ["06:03:00"],
"Max" => ["01:04:00", "04:10:12"],
"Serto" => ["02:00:02"],
]
],
"B" => [
"color" => 'blue',
"user" => [
"Franz" => ["08:40:52", "14:05:32", "20:34:15"],
"Hugo" => ["07:08:58", "04:10:12"],
]
],
"C" => [
"color" => 'green',
"user" => [
"Maxi" => ["07:08:58", "04:10:12"],
]
]
];

You cannot simply call array_merge_recursive() on the whole data sets because they will generated repeated color values, but you want the color values to remain singular and the user data to be recursively merged.
To accommodate this logic (assuming it is okay to simply mutate the $existingtArr array with the data from $newArr), perform a check for the existence of each letter-set, and either push the whole set for a non-present letter, or recursively merge the shared letter-sets.
Code: (Demo)
foreach ($newArr as $letter => $set) {
if (!isset($existingtArr[$letter])) {
$existingtArr[$letter] = $set;
} else {
$existingtArr[$letter]['user'] = array_merge_recursive(
$existingtArr[$letter]['user'],
$set['user']
);
}
}
var_export($existingtArr);

Related

How to add unique identifier to each level of nested array with unknown values?

I have array that contains 4 levels of nesting:
$data = [
"" => [
'sub_level_1' => [
'sub_level_2' => [
'sub_level_3' => [
0 => ""
1 => "val"
]
]
]
],
"top_level_2" => [
'sub_level_1' => [
'sub_level_2' => [
'sub_level_3' => [
0 => ""
1 => "some_val"
2 => "foo"
]
]
]
]
];
I want to add other unique identifier for each nested level, so I use a counter on each level:
$top_level_counter = 0;
$sub_level_1_counter = 0;
$sub_level_2_counter = 0;
$sub_level_3_counter = 0;
foreach ($data as &$top_level)
{
$top_level['top_level_id'] = $top_level_counter++;
foreach($top_level as &$sub_level_1)
{
$sub_level_1['sub_level_1_id'] = $sub_level_1_counter++;
foreach($sub_level_1 as &$sub_level_2)
{
$sub_level_2['sub_level_2_id'] = $sub_level_2_counter++;
// and continue with the last level..
}
}
}
But as the example above, the keys can be empty ("") or even null because that's data from the database.
I end up getting errors related to invalid values, but I can't find out which.
I did figure that as I go inside each level, I have to check for:
if (is_array($sub_level_<x>)
because on each level above I already store the unique id which is not an array. But still, even with that, I get invalid input for foreach for example.
But I can't find out what's causing that, though I suspect it might be because of a null or empty value
Is there a better way to do that?
The reason I want to do it is to have an easier to work with identifier for each element in the array because the actual data is strings in another language.
Expected result would be:
$data = [
"" => [
'top_level_id' => 0,
'sub_level_1' => [
'sub_level_1_id' => 0,
'sub_level_2' => [
'sub_level_2_id' => 0,
'sub_level_3' => [
0 => ""
1 => "val"
]
]
]
],
"top_level_2" => [
'top_level_id' => 1,
'sub_level_1' => [
'sub_level_1_id' => 1,
'sub_level_2' => [
'sub_level_2_id' => 1,
'sub_level_3' => [
0 => ""
1 => "some_val"
2 => "foo"
]
]
]
]
];

Compare some keys from 2 multidimensional arrays

I need to compare the value of the keys from 2 multidimensional arrays :
array1 [
0 => [
"designation" => "multiple"
"type" => "AAAAA"
"model" => "B"
"isSim" => false
"order" => 5
]
etc...
]
array2 [
0 => [
"designation" => "single"
"type" => "AACAA"
"model" => "B"
]
etc...
]
I would like to compare 'designation', 'type', 'model' from array1 to array2 and if the values are the same, I will set the 'isSim' to true.
I know how to set 'isSim' but I've got some difficulties to compare the 2 multidimensional arrays
NOTE => The 2 arrays don't have the same size
Should be something like the following:
$basearray = [["designation" => "multiple",
"type" => "AAAAA",
"model" => "B",
"isSim" => false,
"order" => 5 ]];
$compareto = [["designation" => "single"
"type" => "AACAA"
"model" => "B"]];
foreach($basearray as $base){
foreach($compareto as $compare){
if($compare["designation"] == $base["designation"] &&
$compare["type"] == $base["type"] &&
$compare["model"] == $base["model"]){
$base["isSim"] = true;
}
}
}
Additionally if you are sure that there can only be one array in $compareto that is the same as an array from $basearray you can put a break; after $base["isSim"] = true;.

PHP - Get the key that has a value in multiple indexes

I have the below array:
$myArray = [
[
"name" => null,
"price" => [
"height" => 0.0098974902792506,
"left" => 0.8385,
"page" => 1,
"top" => 0.51290208554259,
"width" => 0.0275,
],
],
[
"name" => null
"price" => [
"height" => 0.0098974902792506,
"left" => 0.838,
"page" => 1,
"top" => 0.56981265464829,
"width" => 0.028,
]
],
[
"name" => null
"price" => [
"height" => 0.010250972074938,
"left" => 0.5905,
"page" => 1,
"top" => 0.44114528101803,
"width" => 0.0285,
]
]
];
I am trying to check the array and get the name of the key that has a value (is not null) in each array. In the above example, this would be price.
However, the array could also look like this:
[
[
"name" => null,
"price" => [
"height" => 0.0098974902792506,
"left" => 0.8385,
"page" => 1,
"top" => 0.51290208554259,
"width" => 0.0275,
],
],
[
"name" => null
"price" => null
],
[
"name" => null
"price" => null
]
]
In this case, there is not an array key that has a value in all of the arrays.
Below is my attempt to achieve this:
$originalKeyWithValue = null;
foreach($myArray as $key => $item)
{
$originalKeyWithValue = array_key_first($item);
if (isset($myArray[$key+1])) {
$nextKeyWithValue = array_key_first($myArray[$key+1]);
if($originalKeyWithValue != $nextKeyWithValue){
$originalKeyWithValue = $nextKeyWithValue;
}
}
}
return $originalKeyWithValue;
However the code above returns name as the key, even though it is null in all of the arrays in $myArray.
This is what would I do:
// I take first element of array as a source for indexes
foreach ($myArray[0] as $index => $item) {
// next I extract all elements from all subarrays under current `$index`
$values = array_column($myArray, $index);
// then I filter values to remove nulls.
// This also removes 0, empty arrays, false,
// so maybe you should change filter process
$values_filtered = array_filter($values);
// if number of filtered items is same as in original array - no nulls found
if (count($values_filtered) === count($values)) {
echo $index;
// optionally
// break;
}
}
Although there is an accepted answer, I thought I would share a way to do this using Laravel collections.
$uniqueKeysWithValues = collect($myArray)->map(function($item){
return array_keys( collect($item)->filter()->toArray() ); //filter will remove all null
})->flatten()->unique();
This approach will give you all keys that has values in it, even if there are values in both keys.

Laravel - restructuring array for easy sync of many-to-many with additional pivot data

I have created what feels like a clunky solution to restructuring a data array in order to pass it to a sync() to update a many-to-many relationship with additional data in the pivot table and wondered if anyone could suggest a simpler approach.
I have an array coming from a request, here's a relevant extract:
"papers" => [
0 => [
"id" => 2
"code" => "123-321-888"
"name" => "Pop out"
"pivot" => [
"job_id" => 46
"paper_id" => 2
"qty_required" => 500
]
]
1 => [
"id" => 1
"code" => "444-666-999"
"name" => "Premium pro"
"pivot" => [
"job_id" => 46
"paper_id" => 1
"qty_required" => 1000
]
]
]
In order to do an easy sync of a many-to-many relationship with extra pivot data one needs to restructure that to:
[
paper[id] => [
'qty_required' => paper[pivot][qty_required]
]
]
Which for the above example would be:
[
2 => [
"qty_required" => "500"
]
1 => [
"qty_required" => "1000"
]
]
I'm currently doing a 2-step process to achieve this as follows:
$paperUpdate = Arr::pluck($request->input('papers'), 'pivot.qty_required', 'id');
//output: [ 2 => 500, 1 => 1000]
foreach ($paperUpdate as $key => $value) {
$paperSync[$key]['qty_required'] = $value;
}
//output: [ 2 => [ "qty_required" => "500" ], 1 => [ "qty_required" => "1000" ]
$job->papers()->sync($paperSync);
Is there an easier approach?
Your approach seems fine to me. If you want to nit pick, you could do one less iteration using:
$sync = array_reduce($request->input('papers'), function ($sync, $paper) {
$id = $paper['id'];
$sync[$id] = [ 'qty_required' => $paper['pivot']['qty_required'] ];
return $sync;
}, []);

Removing an element from a nested array in PHP

Am working on a Laravel application whereby I have an associative array that am to pass to an API endpoint, Before posting to the API, I want to delete the img key together with its value . I have tried to use unset function but it is not removing the img key
Array where I want to remove the image property
$a[] = [
0 => array:4 [
"name" => "Martoo nnn"
"relationship" => "Spouse"
"dob" => "2001-02-03"
"img" => "img.png"
]
1 => array:4 [
"name" => "sdsdsd sdsdsd"
"relationship" => "Child"
"dob" => "2019-04-04"
"img" => "img1.png"
]
2 => array:4 [
"name" => "sdsdsd sddds"
"relationship" => "Child"
"dob" => "2019-04-05"
"img" => "img2.png"
]
3 => array:4 [
"name" => "dssdsd dsdsd"
"relationship" => "Child"
"dob" => "2019-04-02"
"img" => "img3.png"
]
4 => array:4 [
"name" => "dssdsd dssdsd"
"relationship" => "Child"
"dob" => "2019-04-04"
"img" => "img4.png"
]
];
Unset method
$array = $a;
unset($array['img']);
//dd($a);
You can do something like this,
foreach ($array as $key => &$value) { // & defines changes will be made # value itself
unset($value['img']);
}
And Yes, I don't understand why you initialised $a as $a[]?
$newarray = array_filter($a, function($k) {
return $k != 'img';
}, ARRAY_FILTER_USE_KEY);
and pass this new array

Categories