Restructure multidimensional array based on associative array key - php

I have the following array:
$products = [
[
'id' => 21,
'name' => 'Floral Dress',
'params' => [
'size' => 14,
'price' => 23.99,
'department' => 'Dresses'
]
],
[
'id' => 413,
'name' => 'Maxi Skirt',
'params' => [
'size' => 10,
'price' => 12.99,
'department' => 'Skirts'
]
],
[
'id' => 78,
'name' => 'A Line Prom Dress',
'params' => [
'size' => 10,
'price' => 79.99,
'department' => 'Dresses'
]
],
[
'id' => 212,
'name' => 'Nude Block High Heels',
'params' => [
'size' => 6,
'price' => 20.99,
'department' => 'Shoes'
]
],
[
'id' => 54,
'name' => 'Denim Trim Dress',
'params' => [
'size' => 8,
'price' => 52.99,
'department' => 'Dresses'
]
],
];
Unfortunately I cannot control how the array is built.
Instead I need to restructure the data so that the array is sorted by the ['params']['department'] key.
So ideally I would take the above and output it like so:
$products = [
'Dresses' => [
[
'id' => 21,
'name' => 'Floral Dress',
'params' => [
'size' => 14,
'price' => 23.99
]
],
[
'id' => 78,
'name' => 'A Line Prom Dress',
'params' => [
'size' => 10,
'price' => 79.99
]
],
[
'id' => 54,
'name' => 'Denim Trim Dress',
'params' => [
'size' => 8,
'price' => 52.99
]
],
],
'Skirts' => [
[
'id' => 413,
'name' => 'Maxi Skirt',
'params' => [
'size' => 10,
'price' => 12.99
]
],
],
'Shoes' => [
[
'id' => 212,
'name' => 'Nude Block High Heels',
'params' => [
'size' => 6,
'price' => 20.99
]
],
]

You could simply iterate over all the products and populate a new associative array holding the departments, which are themselves arrays of corresponding products. See the comments in the code below.
<?php
// same data as in the question, just in one line for better overview
$products = [['id' => 21,'name' => 'Floral Dress','params' => ['size' => 14,'price' => 23.99,'department' => 'Dresses']],['id' => 413,'name' => 'Maxi Skirt','params' => ['size' => 10,'price' => 12.99,'department' => 'Skirts']],['id' => 78,'name' => 'A Line Prom Dress','params' => ['size' => 10,'price' => 79.99,'department' => 'Dresses']],['id' => 212,'name' => 'Nude Block High Heels','params' => ['size' => 6,'price' => 20.99,'department' => 'Shoes']],['id' => 54,'name' => 'Denim Trim Dress','params' => ['size' => 8,'price' => 52.99,'department' => 'Dresses']]];
// initialize empty department array
$departments = array();
// iterate over all the products
foreach ( $products as $product ) {
// get this product's department
$product_department = $product['params']['department'];
// unset the department in the params array
unset( $product['params']['department'] );
// initialize this department as an empty array
// if it doesn't exist yet
if ( !isset( $departments[$product_department] ) ) {
$departments[$product_department] = array();
}
// push this product in its corresponding department
$departments[$product_department][] = $product;
}
var_dump( $departments );

$groupedProducts = [];
foreach ($products as &$product) {
$department = $product['params']['department'];
unset($product['params']['department']);
$groupedProducts[$department] = $product;
}
var_dump($groupedProducts);

Related

How I can flatten a tree in an orderly way?

I have PHP array (tree), something like this:
$categoryTree = [
0 => [
'id' => 1360,
'parent' => 0,
'name' => 'main A',
'children' => [
0 => [
'id' => 1361,
'parent' => 1360,
'name' => 'sub a1'
],
1 => [
'id' => 57,
'parent' => 1360,
'name' => 'sub a2'
]
]
],
1 => [
'id' => 10,
'parent' => 0,
'name' => 'Main B'
]
];
I want to convert it into:
$categoryTree = [
0 => [
'id' => 1360,
'parent' => 0,
'name' => 'main A'
],
1 => [
'id' => 1361,
'parent' => 1360,
'name' => 'sub a1'
],
2 => [
'id' => 57,
'parent' => 1360,
'name' => 'sub a2'
],
3 => [
'id' => 10,
'parent' => 0,
'name' => 'Main B'
]
];
It is rather simple. You walk recursively and only make a recursive call if the node has the key children. During the iteration in the foreach loop, keep collecting results in an result array.
<?php
function flatten($tree, &$results){
foreach($tree as $kid){
$kid_copy = $kid;
unset($kid_copy['children']);
$results[] = $kid_copy;
if(isset($kid['children'])) flatten($kid['children'], $results);
}
}
Online Demo
Alternatively, using a generator, which then doesn't need the second argument:
function recurIter($arr) {
foreach ($arr as $item) {
$orig = $item;
unset($item["children"]);
yield $item;
if (isset($orig["children"])) yield from recurIter($orig["children"]);
}
}
$result = iterator_to_array(recurIter($categoryTree), false);

Sorting an array by text of first subarray in each row

I have an array stored as $product_categories. A sample of this array is:
$array = [
[
['id' => 10, 'text' => 'Latex'],
['id' => 15, 'text' => 'Occasion Latex'],
['id' => 82, 'text' => 'Christmas'],
],
[
['id' => 11, 'text' => 'Accessories'],
['id' => 97, 'text' => 'Retail Accessories'],
['id' => 558, 'text' => 'Super Stuffer'],
],
[
['id' => 374, 'text' => 'Party Supplies'],
['id' => 1488, 'text' => 'Party by Occasion'],
['id' => 1493, 'text' => 'Christmas'],
],
];
I want to sort it ONLY by the key 'text' in [0], which would give me a result of
[
[
['id' => 11, 'text' => 'Accessories'],
['id' => 97, 'text' => 'Retail Accessories'],
['id' => 558, 'text' => 'Super Stuffer'],
],
[
['id' => 10, 'text' => 'Latex'],
['id' => 15, 'text' => 'Occasion Latex'],
['id' => 82, 'text' => 'Christmas'],
],
[
['id' => 374, 'text' => 'Party Supplies'],
['id' => 1488, 'text' => 'Party by Occasion'],
['id' => 1493, 'text' => 'Christmas'],
],
];
I've tried using
$product_categories = usort($product_categories, 'sortAlphabetically');
function sortAlphabetically($a, $b) {
return strcmp($a['text'], $b['text']);
}
Using that, a print_r() of the array simply returns
1.
I thought usort() was the correct way to sort the array but clearly I'm doing something wrong here.
You only need to access the subarray data using array syntax as you've expressed in English. (Demo)
usort(
$array,
function($a, $b) {
return $a[0]['text'] <=> $b[0]['text'];
}
);
var_export($array);
Or in PHP7.4 or higher:
usort($array, fn($a, $b) => $a[0]['text'] <=> $b[0]['text']);
Your array:
$array = [
'0' => [
'0' => [
'id' => 10,
'text' => 'Latex',
],
'1' => [
'id' => 15,
'text' => 'Occasion Latex',
],
'2' => [
'id' => 82,
'text' => 'Christmas',
],
],
'1' => [
'0' => [
'id' => 11,
'text' => 'Accessories',
],
'1' => [
'id' => 97,
'text' => 'Retail Accessories',
],
'2' => [
'id' => 558,
'text' => 'Super Stuffer',
],
],
'2' => [
'0' => [
'id' => 374,
'text' => 'Party Supplies',
],
'1' => [
'id' => 1488,
'text' => 'Party by Occasion',
],
'2' => [
'id' => 1493,
'text' => 'Christmas',
],
],
];
The sort:
// uasort(): "Sort an array with a user-defined comparison function
// and maintain index association"
uasort($array, function (array $a, array $b) {
// "I want to sort it ONLY by the key 'text' in [0]"
// Get first from array (aka [0]).
// (You could change that to fix $a[0] and $b[0] if you want|need to.)
$aFirst = reset($a);
$bFirst = reset($b);
$offset = 'text';
if ($aFirst[$offset] == $bFirst[$offset]) {
return 0;
}
// a < b === asc ; a > b === desc
return ($aFirst[$offset] < $bFirst[$offset]) ? -1 : 1;
});
echo var_export($array, true) . PHP_EOL;
Results in:
[
1 => [
0 => [
'id' => 11,
'text' => 'Accessories',
],
1 => [
'id' => 97,
'text' => 'Retail Accessories',
],
2 => [
'id' => 558,
'text' => 'Super Stuffer',
],
],
0 => [
0 => [
'id' => 10,
'text' => 'Latex',
],
1 => [
'id' => 15,
'text' => 'Occasion Latex',
],
2 => [
'id' => 82,
'text' => 'Christmas',
],
],
2 => [
0 => [
'id' => 374,
'text' => 'Party Supplies',
],
1 => [
'id' => 1488,
'text' => 'Party by Occasion',
],
2 => [
'id' => 1493,
'text' => 'Christmas',
],
],
]

How to transform nested array to a flat array of nodes PHP?

I have a nested array with a structure as detailed below.
It is an org chart where an employ could have other employees related:
$tree_array = [
[
'id' => 1,
'employee' => 'John',
'leader_id' => NULL,
'team' => [
[
'id' => 2,
'employee' => 'Maria',
'leader_id' => 1,
'team' => [],
],
[
'id' => 3,
'employee' => 'Kevin',
'leader_id' => 1,
'team' => [
[
'id' => 4,
'employee' => 'Alan',
'leader_id' => 3,
'team' => [],
],
[
'id' => 5,
'employee' => 'Bret',
'leader_id' => 3,
'team' => [],
],
],
],
],
]
];
Every node has an ID and a team, that could be an array of other nodes and so on.
I need to obtain each node in a flat array, like this structure:
$flat_array = array(
array(
'id' => 1,
'employee' => 'John',
'leader_id' => NULL
),
array(
'id' => 2,
'employee' => 'Maria',
'leader_id' => 1
),
array(
'id' => 3,
'employee' => 'Kevin',
'leader_id' => 1
),
...
);
I've tried to implement a recursive function, but I get only the nodes of the last iteration.
Any help?
You can make a recursive function, like this:
function flatten($arr, $final=array()) {
foreach($arr as $a) {
$tmp = $a;
unset($tmp['team']);
$final[]= $tmp;
if ($a['team'] && count($a['team']) > 0) $final = flatten($a['team'], $final);
}
return $final;
}
$flat =flatten($tree_array);
Test it here: https://www.tehplayground.com/UHWCccFIkrS5v75Z

Insert new object into objects tree. How?

I have just finished creating this beautiful php object tree. How would I go about inserting new objects using a loop into the items node array? Basically how do I insert new anonymous item objects into the items array in this object tree?
Take a look
<?php
$phpObjectTree =
(object)[
'apiVersion' => '1.0',
'data' => (object)[
'requested' => '2020-01-01T23:59:59.001Z',
'status' => 'OK',
'estimate' => (object)[
'id' => '1001',
'type' => 'Commercial',
'client' => (object)[
'name' => 'ABC Construction, Inc.',
'address1' => '123 Main St',
'city' => 'Salt Lake City',
'state' => 'UT',
'postalCode' => '84101'
],
'summary' => (object)[
'generalRequirements' => (object)[
'default' => 0
],
'salesTax' => (object)[
'material' => 4.85,
'labor' => 4.85,
'equipment' => 4.85
],
'overheadProfit' => (object)[
'material' => 10,
'labor' => 10,
'equipment' => 10
],
'contingency' => (object)[
'default' => 3
],
'performanceBond' => (object)[
'default' => 1.3
],
'laborPlus' => (object)[
'dailyRate' => 280,
'crewCount' => 0,
'dayCount' => 0
],
'locationAdjustment' => (object)[
'factor' => 90.1
],
]
],
'items' => [
(object)[
'id' => '2001',
'number' => '09 91 23.00 0000',
'description' => 'Line Item 1',
'quantity' => 0,
'unit' => 'sf',
'material' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'labor' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'equipment' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'lineTotal' => 0
],
(object)[
'id' => '2002',
'number' => '09 91 23.00 0000',
'description' => 'Line Item 2',
'quantity' => 0,
'unit' => 'sf',
'material' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'labor' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'equipment' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'lineTotal' => 0
]
]
]
];
// SET JSON HEADER...
header('Content-type:application/json;charset=utf-8');
print_r(json_encode($phpObjectTree));
?>
Same way you'd modify any array in php:
https://www.php.net/manual/en/language.types.array.php#language.types.array.syntax.modifying
$phpObjectTree->data->items[] = (object)[
'id' => '2003',
'number' => '09 91 23.00 0000',
'description' => 'Line Item 3',
'quantity' => 0,
'unit' => 'sf',
'material' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'labor' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'equipment' => (object)[
'cost' => 0,
'costEach' => 9.99
],
'lineTotal' => 0
];
Or you can use stdClass to create new object and add properties.
$obj = new \stdClass;
$obj->newProperty = 'value';

PHP push a value to an array during recursive search

$menus = [
0 => [
'id' => 'home',
'title' => 'Home',
'url' => '/display/home',
'children' => [],
'parent' => null
],
1 => [
'id' => 'nodes',
'title' => 'Nodes',
'url' => 'nodes/index',
'children' => [
0 => [
'id' => 'addNode',
'title' => 'Add Node',
'url' => '/nodes/add',
'children' => [],
'parent' => "nodes"
],
1 => [
'id' => 'editNode',
'title' => 'Edit Node',
'url' => '/nodes/edit',
'children' => [],
'parent' => 'nodes'
],
2 => [
'id' => 'deleteNode',
'title' => 'Delete Node',
'url' => '/nodes/delete',
'children' => [
0 => [
'id' => 'deleteMultipleNodes',
'title' => 'Delete Multiple Nodes',
'url' => '/nodes/deleteall',
'children' => [
0 => [
'id' => 'deleteMultipleSelectedNodes',
'title' => 'Delete Multiple Selected Nodes',
'url' => '/nodes/deleteallselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
]
],
'parent' => 'deleteNode'
]
],
'parent' => 'nodes'
]
],
'parent' => null
]
];
Assuming I have this array. What i want is to recursively search this array for an "id" and if found push a new children to the children array of that element.
I've tried it via different ways, I've also tried to use RecursiveArrayIterator to traverse the array, but the problem is how can i push value to that index of the array when found while traversing.
For Example here is a code from one of my tries:
private function traverseArray($array)
{
$child = [
'id' => 'deleteMultipleNotSelectedNodes',
'title' => 'Delete Multiple Not Selected Nodes',
'url' => '/nodes/deletenotselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
];
foreach($array as $key=>$value)
{
if(is_array($value))
{
$this->traverseArray($value);
}
if($key == "id" && $value == "deleteMultipleNodes")
{
array_push($array["children"], $child); // This part is confusing me, How to add the child on this index where the id is found.
}
}
}
Any help on how to do such thing in an efficient way would save my days.
Here it's how it would work without using $this and fixing bugs in comparing $value instead assigning anything to value.
Please note the difference with &$array and &$value, which are references, so it would change the original data instead of copying it into new variables.
<?php
$menus = [
0 => [
'id' => 'home',
'title' => 'Home',
'url' => '/display/home',
'children' => [],
'parent' => null
],
1 => [
'id' => 'nodes',
'title' => 'Nodes',
'url' => 'nodes/index',
'children' => [
0 => [
'id' => 'addNode',
'title' => 'Add Node',
'url' => '/nodes/add',
'children' => [],
'parent' => "nodes"
],
1 => [
'id' => 'editNode',
'title' => 'Edit Node',
'url' => '/nodes/edit',
'children' => [],
'parent' => 'nodes'
],
2 => [
'id' => 'deleteNode',
'title' => 'Delete Node',
'url' => '/nodes/delete',
'children' => [
0 => [
'id' => 'deleteMultipleNodes',
'title' => 'Delete Multiple Nodes',
'url' => '/nodes/deleteall',
'children' => [
0 => [
'id' => 'deleteMultipleSelectedNodes',
'title' => 'Delete Multiple Selected Nodes',
'url' => '/nodes/deleteallselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
]
],
'parent' => 'deleteNode'
]
],
'parent' => 'nodes'
]
],
'parent' => null
]
];
function traverseArray(&$array)
{
$child = [
'id' => 'deleteMultipleNotSelectedNodes',
'title' => 'Delete Multiple Not Selected Nodes',
'url' => '/nodes/deletenotselected',
'children' => [],
'parent' => 'deleteMultipleNodes'
];
foreach($array as $key=>&$value)
{
if(is_array($value))
{
traverseArray($value);
}
if($key == "id" && $value == "deleteMultipleNodes")
{
array_push($array["children"], $child);
}
}
}
echo "=== before \n";
var_export($menus);
echo "\n\n";
traverseArray($menus);
echo "=== after \n";
var_export($menus);

Categories