I have a collection of data.
$array = [
[
'id' => 1,
'name' => 'some1',
'type' => 'type1',
'color' => 'color1',
'quantity' => 1
],
[
'id' => 2,
'name' => 'some1',
'type' => 'type1',
'color' => 'color1',
'quantity' => 1
],
[
'id' => 3,
'name' => 'some1',
'type' => 'type1',
'color' => 'color2',
'quantity' => 1
],
[
'id' => 4,
'name' => 'some2',
'type' => 'color1',
'color' => 'type1',
'quantity' => 1
],
......
];
that have different name, type, and color.
I want to group the data by name, type, and color and the result is array data with summary of the same group data.
First, I use this way :
function groupedData($array)
{
$collection = [];
collect($array)->groupBy('name')->map(
function ($item) use (&$collection) {
return $item->groupBy('type')->map(
function ($item) use (&$collection) {
return $item->groupBy('color')->map(
function ($item) use (&$collection) {
$quantity = $item->sum('quantity');
$collection[] = collect($item[0])->merge(compact('quantity'));
}
);
}
);
}
);
return $collection;
}
I expect the output should be like this :
$grouped = [
[
'id' => 1,
'name' => 'some1',
'type' => 'type1',
'color' => 'color1',
'quantity' => 2
],
[
'id' => 2,
'name' => 'some1',
'type' => 'type1',
'color' => 'color2',
'quantity' => 1
],
[
'id' => 3,
'name' => 'some2',
'type' => 'type1',
'color' => 'color1',
'quantity' => 2
],
[
'id' => 4,
'name' => 'some2',
'type' => 'type2',
'color' => 'color1',
'quantity' => 2
],
];
where quantity represent of the number of group items.
But, I the problem is when the required is changed. Just in case :
when user want add other category for grouping, example :
user want to group by name, type, color and size maybe.
Question : How to make a function that can make it more simple and flexible, so doesn't need to change the code when the require changes?
Thanks for the answer.
What you're looking for it sorting, not grouping.
Here's an easy way to do it:
function sort($array, $keys) {
return collect($array)->sortBy(function ($item) use ($keys) {
return array_reduce($keys, function ($carry, $key) use ($item) {
return $carry + $item[$key];
}, '');
})->all();
}
Here's a short explanation:
We're using the collection's sortBy method, which let's us use a callback function that'll return a string to determine the sort order.
We're using array_reduce on the keys to build a single string of all the values in the keys we want to sort by.
Laravel's collection object will use that resulting string to order the collection by.
Finally, we call the all method to get the underlying array from the collection. If you want to actually return a collection, you can remove that last all call.
Related
$arr = [
[
"id" => '6230061c0e88d709ca0d7bbc',
'name' => 'Mobile SamSung',
'slug' => 'mobile-samsung',
'createdAt' => '1648006346'
],
[
"id" => '5d1eff529a426778d4b92383',
'name' => 'Mobile Iphone',
'slug' => 'mobile-iphone',
'createdAt' => '1647314181'
],
[
"id" => '5d1eff6b9a426778d4b92dc4',
'name' => 'Mobile SamSung',
'slug' => 'mobile-samsung',
'createdAt' => '1647314460'
],
[
"id" => '5f894011266aea580b028cb0',
'name' => 'Mobile LG',
'slug' => 'mobile-lg',
'createdAt' => '1647314456'
]
];
I have an array, and in this array there are many duplicate subarrays, now I want to remove the duplicate arrays inside, keeping only the data with the latest createdAt. Please give me your opinion. Thanks
I would like to get an array like this:
$arr = [
[
"id" => '6230061c0e88d709ca0d7bbc',
'name' => 'Mobile SamSung',
'slug' => 'mobile-samsung',
'createdAt' => '1648006346'
],
[
"id" => '5d1eff529a426778d4b92383',
'name' => 'Mobile Iphone',
'slug' => 'mobile-iphone',
'createdAt' => '1647314181'
],
[
"id" => '5f894011266aea580b028cb0',
'name' => 'Mobile LG',
'slug' => 'mobile-lg',
'createdAt' => '1647314456'
]
];
You should not make more than one pass over your data. Just use the name values as temporary keys, then only retain a duplicate row's data if its createAt value is greater than what is stored. Re-index the array when you are finished looping.
Code: (Demo)
$result = [];
foreach ($arr as $row) {
if (!isset($result[$row['name']]) || (int)$row['createdAt'] > (int)$result[$row['name']]['createdAt']) {
$result[$row['name']] = $row;
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => '6230061c0e88d709ca0d7bbc',
'name' => 'Mobile SamSung',
'slug' => 'mobile-samsung',
'createdAt' => '1648006346',
),
1 =>
array (
'id' => '5d1eff529a426778d4b92383',
'name' => 'Mobile Iphone',
'slug' => 'mobile-iphone',
'createdAt' => '1647314181',
),
2 =>
array (
'id' => '5f894011266aea580b028cb0',
'name' => 'Mobile LG',
'slug' => 'mobile-lg',
'createdAt' => '1647314456',
),
)
Potentially helpful:
Laravel - fetch unique rows from table having highest value in x column
Remove duplicate objects from array based on one value, keep lowest of other value in PHP?
Filter rows with unique column value and prioritize rows with a particular value in another column
How to get max amount of value in same key in array
Explanation:
In this solution, I have gotten the data with a unique slug key with the latest createdAt key. we can have any unique key that matches into the multidimensional array and get the result whatever we want.
Code:
$newArray = [];
foreach ($array as $key => $value) {
$findIndex = array_search($value['slug'], array_column($newArray, 'slug'));
if ($findIndex === false) {
$newArray[] = $value;
} elseif ($findIndex !== false && $newArray[$findIndex]['createdAt'] <= $value['createdAt']) {
$newArray[$findIndex] = $value;
}
}
print_r($newArray);
Demo Link (With your Data): https://3v4l.org/f4kRM
Demo Link (Customized Data with my way): https://3v4l.org/sj4MW
First sort on created at, then remove duplicates.
<?php
$arr = [
[
"id" => '6230061c0e88d709ca0d7bbc',
'name' => 'Mobile SamSung',
'slug' => 'mobile-samsung',
'createdAt' => '1648006346'
],
[
"id" => '5d1eff529a426778d4b92383',
'name' => 'Mobile Iphone',
'slug' => 'mobile-iphone',
'createdAt' => '1647314181'
],
[
"id" => '5d1eff6b9a426778d4b92dc4',
'name' => 'Mobile SamSung',
'slug' => 'mobile-samsung',
'createdAt' => '1647314460'
],
[
"id" => '5f894011266aea580b028cb0',
'name' => 'Mobile LG',
'slug' => 'mobile-lg',
'createdAt' => '1647314456'
]
];
function sort_objects_by_created($a, $b) {
if($a['createdAt'] == $b['createdAt']){ return 0 ; }
return ($a['createdAt'] > $b['createdAt']) ? -1 : 1;
}
// Let's sort
usort($arr, 'sort_objects_by_created');
$slugs = [];
$result = [];
// Loop object
foreach($arr as $phone) {
// If slug is not found, add to result
if (!in_array($phone['slug'], $slugs)){
$slugs[] = $phone['slug'];
$result[] = $phone;
}
}
var_dump($result,$slugs);
Might be worth a note, that you might be able to improve this upstream when creating your array. (always look upstream!)
If you can give you base array a key of created At you can use Array sorting, which which will this step more effecient....
E.g.
$arr = [];
$arr[2022-01-01] = Array('id' => 123, 'name' = 'abc');
$arr[2022-04-01] = Array('id' => 123, 'name' = 'abc');
$arr[2022-08-01] = Array('id' => 123, 'name' = 'abc');
I have an array with 3 levels and I'd like to merge/flatten all 3rd level subarrays into one subarray/row on the 2nd level.
$array = [
[
'User' => [
'id' => 57341,
'updated' => null,
'userId' => 57341,
'value' => null,
'lat' => 53.4537812,
'lon' => -2.1792437,
],
[
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
]
],
[
'User' => [
'id' => 57336,
'updated' => null,
'userId' => 57336,
'value' => null,
'lat' => 53.473684,
'lon' => -2.2399827,
],
[
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
]
],
];
The deep User-keyed subarrays (having 6 elements) should be merged with its sibling/indexed subarray (having 3 elements) to form a 9-element row on the second level.
Desired result:
[
[
'id' => 57341,
'updated' => null,
'userId' => 57341,
'value' => null,
'lat' => 53.4537812,
'lon' => -2.1792437,
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
],
[
'id' => 57336,
'updated' => null,
'userId' => 57336,
'value' => null,
'lat' => 53.473684,
'lon' => -2.2399827,
'feed_likes' => 'NA',
'category_idx' => -1,
'type' => 'User'
]
]
You can use splat ... operator with array_merge
foreach($a as $child){
$flatten[] = array_merge(...$child);
}
Working example :- https://3v4l.org/HkUh6
To merge and flatten the 3rd level data sets into consolidated 2nd level rows with functional style programming, make iterated calls of array_merge() which receive all 3rd level payloads at once. The spread operator (...) is a concise technique used to unpack multiple elements in an array. A special consideration is needed for this case because spreading elements which have non-numeric keys will cause code breakage. To overcome this, simply call array_values() to "index" the array (replace all keys with sequenial numbers) before spreading.
Code: (Demo)
var_export(
array_map(
fn($rows) => array_merge(...array_values($rows)),
$array
)
);
Slightly different to your example output, but you can merge your inner arrays.
<?php
$data =
[
[
'animal' => [
'type' => 'fox'
],
[
'colour' => 'orange'
]
],
[
'animal' => [
'type' => 'panda'
],
[
'colour' => 'black and white'
]
]
];
$result =
array_map(
function($v) {
return array_merge($v['animal'], $v[0]);
},
$data
);
var_export($result);
Output:
array (
0 =>
array (
'type' => 'fox',
'colour' => 'orange',
),
1 =>
array (
'type' => 'panda',
'colour' => 'black and white',
),
)
If I understood you correctly you need to merge User array and in this case with the second array in that key. it that case something like this should work
foreach($array as $key=>$deep1){
$newArray = [];
foreach($deep1 as $deep2){
$newArray = array_merge($newArray,$deep2)
}
$array[$key] = $newArray;
}
Did II understand your question correctly?
i need to pass My custom data to the same level of array of resource but it goes out of array
return CategoryProductsResource::collection(Category::whereNull('parent_id')->get())
->additional([
'data' => [
'id' => 9999,
'name' => 'How Offers',
'image' => 'http://businessdotkom.com/storage/categories/January2020/1o6nDi1kjVuwje5FiFXv.png',
'products' => ProductIndexResource::collection(Product::whereNotNull('sale_price')->get()),
]
]);
json output
Because additional is for top-level data, use concat instaed.
return CategoryProductsResource::collection(Category::whereNull('parent_id')->get())
->concat([
'data' => [
'id' => 9999,
'name' => 'How Offers',
'image' => 'http://businessdotkom.com/storage/categories/January2020/1o6nDi1kjVuwje5FiFXv.png',
'products' => ProductIndexResource::collection(Product::whereNotNull('sale_price')->get()),
]
]);
You may use PHP array_merge() function
$categories = Category::whereNull('parent_id')->get()->toArray();
$merged = array_merge($categories, [
[
'id' => 9999,
'name' => 'How Offers',
'image' => 'http://businessdotkom.com/storage/categories/January2020/1o6nDi1kjVuwje5FiFXv.png',
'products' => ProductIndexResource::collection(Product::whereNotNull('sale_price')->get()),
]
]);
return CategoryProductsResource::collection(collect($merged));
I am trying to convert this tree structure:
{
"id":1,"name":"Corp.","emoji":"🐕","parent_id":null,"children":
[
{"id":2,"name":"Food","emoji":"🥩","parent_id":1,"children":[]},
{"id":3,"name":"Canine+Therapy","emoji":"😌","parent_id":1,"children":
[
{"id":4,"name":"Massages","emoji":"💆","parent_id":3,"children":[]},
{"id":5,"name":"Games","emoji":"🎾","parent_id":3,"children":[]}
]
}
]
}
to this format:
[
[ 'id' => 1, 'name' => 'Corp.', 'emoji' => '🐕' ,'parent_id' => null ],
[ 'id' => 2, 'name' => 'Food', 'emoji' => '🥩', 'parent_id' => 1 ],
[ 'id' => 3, 'name' => 'Canine Therapy', 'emoji' => '😌', 'parent_id' => 1 ],
[ 'id' => 4, 'name' => 'Massages', 'emoji' => '💆', 'parent_id' => 3 ],
[ 'id' => 5, 'name' => 'Games', 'emoji' => '🎾', 'parent_id' => 3 ],
]
Any idea on how to approach this?
Thanks
You can use a recursive function like this:
function collapse($tree, &$collapsed = []) {
$tree = clone $tree;
$collapsed[] = $tree;
foreach ($tree->children as $child) {
collapse($child, $collapsed);
}
unset($tree->children);
return $collapsed;
}
$new = collapse($tree);
Note that the $collapsed variable that holds the result must be passed by reference for this to work. Each time the function is called the current node is added to the result array, then each of its children is added recursively, then the children property is removed.
clone is necessary in the first line to prevent this function from destroying the original tree when it unsets children. If you don't need the original tree you can omit that.
Working example: https://3v4l.org/U6mHs
I have an array which I'm sure there are some duplicate values in it, I want to search in this array and find the duplicate values and return the key of that array.
let me explain with an example, this is my array:
[
0 => [
'name' => 'name0',
'family' => 'family0',
'email' => 'email0#sample.com',
'rate' => 10
],
1 => [
'name' => 'name1',
'family' => 'family1',
'email' => 'email1#sample.com',
'rate' => 4
],
2 => [
'name' => 'name0',
'family' => 'family0',
'email' => 'email0#sample.com',
'rate' => 6
]
];
Now, I want to search in this array by name, family, and email at the same time and return the key of the parent (in this example 0 and 2). because I want to create a new array like this :
[
0 => [
'name' => 'name0',
'family' => 'family0',
'email' => 'email0#sample.com',
'rate' => [
10,
6
]
],
1 => [
'name' => 'name1',
'family' => 'family1',
'email' => 'email1#sample.com',
'rate' => [
4
]
],
];
How can I do this in PHP?
You can use array-reduce and use the 3 similar fields as keys.
Define a function who create the key and set or add rate:
function combineRate($carry, $item) {
$k = implode('###', array($item['name'], $item['family'], $item['email']));
if (isset($carry[$k]))
$carry[$k]['rate'][] = $item['rate'];
else {
$item['rate'] = [$item['rate']];
$carry[$k] = $item;
}
return $carry;
}
Call it with empty array:
$res = array_values(array_reduce($a, 'combineRate', array()));
Live example: 3v4l