I need to restructure an array containing data in 2 levels and 3 levels. All of the values should be grouped by their indexes, but I need to maintain associative relationships.
Sample input:
$variation = [
"sku" => [
0 => "dSADad",
1 => "ASDAF",
2 => "ASFAS",
],
"Price" => [
0 => "1",
1 => "1",
2 => "1",
],
"Quantity" => [
0 => "123",
1 => "123",
2 => "123434",
],
"attributes" => [
"Color" => [
0 => "5",
1 => "4",
2 => "4",
],
"Size" => [
0 => "3",
1 => "3",
2 => "2",
],
"Material" => [
0 => "7",
1 => "7",
2 => "8",
],
],
];
I want to transform it to be grouped by separate variants. I tried several options but without a successful result. I also tried with JS to add an index to the input before submitting, but it still doesn't work. The only option left is to transform it into PHP.
Desired result:
$variations = [
[
"sku" => "dSADad",
"Price" => "1",
"Quantity" => "123",
"attributes" => [
"Color" => "5",
"Size" => "3",
"Material" => "7",
],
],
[
"sku" => "ASDAF",
"Price" => "1",
"Quantity" => "123",
"attributes" => [
"Color" => "4",
"Size" => "3",
"Material" => "7",
],
],
[
"sku" => "ASFAS",
"Price" => "1",
"Quantity" => "123434",
"attributes" => [
"Color" => "4",
"Size" => "2",
"Material" => "8",
],
],
];
I managed to make this piece of code:
function extractVariation($variations, $key)
{
$variation = [];
foreach ($variations as $property => $values) {
if (isset($values[$key])) {
$variation[$property] = $values[$key];
} else {
$variation[$property] = extractVariation($values, $key);
}
}
return $variation;
}
$newVariations = [];
foreach ($variations['sku'] as $key => $sku) {
$newVariations[] = extractVariation($variations, $key);
}
var_export($newVariations);
See a working example here: https://3v4l.org/l4gJQ
Note that I renamed your $variation array into $variations.
The function is recursive, which allows it to go into the attributes array.
The output is:
array (
0 =>
array (
'sku' => 'dSADad',
'Price' => '1',
'Quantity' => '123',
'attributes' =>
array (
'Color' => '5',
'Size' => '3',
'Material' => '7',
),
),
1 =>
array (
'sku' => 'ASDAF',
'Price' => '1',
'Quantity' => '123',
'attributes' =>
array (
'Color' => '4',
'Size' => '3',
'Material' => '7',
),
),
2 =>
array (
'sku' => 'ASFAS',
'Price' => '1',
'Quantity' => '123434',
'attributes' =>
array (
'Color' => '4',
'Size' => '2',
'Material' => '8',
),
),
)
It is always better to show what you've tried, even if it doesn't work completely. That way people here can see that you're not simply asking them to write code for you, but that you really have a problem.
For your sample data, it is not necessary to use recursion because the input array's depth is known/static.
Traditional array transposition (with a 2d array) is the nesting of two foreach loops and switching the keys between the two levels. With your data, you must conditionally handle the data sets that have 3 levels of depth. Notice the how inside of the is_array() condition, another loop is used to iterate the subarray and push that data into the new appropriate position in the result array.
In all cases, the level containing indexed keys is used as the new first level key and the original first level keys are always used as new second level keys.
Code: (Demo)
$result = [];
foreach ($array as $k1 => $v1) {
foreach ($v1 as $k2 => $v2) {
if (is_array($v2)) {
foreach ($v2 as $k3 => $v3) {
$result[$k3][$k1][$k2] = $v3;
}
} else {
$result[$k2][$k1] = $v2;
}
}
}
var_export($result);
Related
This question already has answers here:
How to add elements to an empty array in PHP?
(8 answers)
Push Array inside an Array PHP
(2 answers)
Closed 28 days ago.
I have this array
echo '<script type="application/ld+json">';
$data = array(
'#context' => 'https://schema.org',
'#graph' => array(),
);
$data['#graph'][] = [
"#type" => "ImageObject",
];
$data['#graph'][] = [
"#type" => "BreadcrumbList",
"itemListElement" => array(),
];
print_r(json_encode($data));
echo "</script>";
Now I want to add another array "itemListElement" inside the last $data['#graph'][] and print but don't know how to go about it.
am expecting
{
"#type": "BreadcrumbList",
"#id": "http:\/\/localhost\/#breadcrumb",
"itemListElement": [{
"#type": "ListItem",
"position": "1",
"item": {
"#id": "http:\/\/localhost",
"name": "Home"
}
}, {
"#type": "ListItem",
"position": "1",
"item": {
"#id": "link 2",
"name": "Home"
}
}]
}
You're referring to nested arrays.
echo '<script type="application/ld+json">';
$data['#graph'][] = [
'#type' => 'BreadcrumbList',
'#id' => "http:\/\/localhost\/#breadcrumb",
'itemListElement' => [
[
'#type' => 'ListItem',
'position' => '1',
'item' => [
'#id' => "http:\/\/localhost",
'name' => 'Home'
]
],
[
'#type' => 'ListItem',
'position' => '2',
'item' => [
'#id' => 'link 2',
'name' => 'Home'
]
]
]
];
print_r(json_encode($data));
echo "</script>";
Good luck on your test
<?php
// PHP program to creating two
// dimensional associative array
$marks = array(
// Ankit will act as key
"Ankit" => array(
// Subject and marks are
// the key value pair
"C" => 95,
"DCO" => 85,
"FOL" => 74,
),
// Ram will act as key
"Ram" => array(
// Subject and marks are
// the key value pair
"C" => 78,
"DCO" => 98,
"FOL" => 46,
),
// Anoop will act as key
"Anoop" => array(
// Subject and marks are
// the key value pair
"C" => 88,
"DCO" => 46,
"FOL" => 99,
),
);
echo "Display Marks: \n";
print_r($marks);
?>
**Output:**
Display Marks:
Array
(
[Ankit] => Array
(
[C] => 95
[DCO] => 85
[FOL] => 74
)
[Ram] => Array
(
[C] => 78
[DCO] => 98
[FOL] => 46
)
[Anoop] => Array
(
[C] => 88
[DCO] => 46
[FOL] => 99
)`enter code here`
)
Having this array :
[
"id" => 5,
"name" => "Item 5",
"all_parents" => [
"id" => 4,
"name" => "Item 4",
"all_parents" => [
"id" => 3,
"name" => "Item 3",
"all_parents" => [
"id" => 2,
"name" => "Item 2",
"all_parents" => [
"id" => 1,
"name" => "Item 1",
"all_parents" => null
]
]
]
]
]
I created a recursive php function that transform that array to this:
[
["id" => 1, "name" => "Item 1"],
["id" => 2, "name" => "Item 2"],
["id" => 3, "name" => "Item 3"],
["id" => 4, "name" => "Item 4"],
["id" => 5, "name" => "Item 5"],
]
The code is this:
private array $breadcrumb = [];
private function generateBreadcrumb($structure) : array
{
if($structure) {
$this->breadcrumb[] = array(
"id" => $structure['id'],
"name" => $structure['name'],
);
$this->generateBreadcrumb($structure['all_parents'] ?? []);
}
return array_reverse($this->breadcrumb);
}
How can I redesign this method without depending on class property $breadcrumb?
By following your initial code, you could do:
function generateBreadcrumb($structure, &$output = []) : array
{
if ($structure) {
$output[] = array(
"id" => $structure['id'],
"name" => $structure['name'],
);
$this->generateBreadcrumb($structure['all_parents'] ?? [], $output);
}
return array_reverse($output);
}
However it could be improved, at least by avoiding to call array_reverse() each time, but only for the root call.
Instead of implementing a recursive function there is the possibility of using the built-in array_walk_recursive function:
$arr = [
'id' => 5,
'name' => 'Item 5',
'all_parents' => [
'id' => 4,
'name' => 'Item 4',
'all_parents' => [
'id' => 3,
'name' => 'Item 3',
'all_parents' => [
'id' => 2,
'name' => 'Item 2',
'all_parents' => [
'id' => 1,
'name' => 'Item 1',
'all_parents' => null
]
]
]
]
];
function generateBreadcrumb($structure): array {
$retval = [];
array_walk_recursive($structure, function ($item, $key) use (&$retval) {
if ($key === 'id') {
$retval[] = [$key => $item];
} elseif ($key === 'name') {
$retval[array_key_last($retval)][$key] = $item;
}
});
return array_reverse($retval);
}
$result = generateBreadcrumb($arr);
Note that array_walk_recursive only visits leafs, so with the exception of the innermost 'all_parents', the other ones are not visited.
A none-recursive version would be this:
function generateBreadcrumb(array $arr): array {
$retval = [];
$temp = &$arr;
do {
$retval[] = [ 'id' => $temp['id'], 'name' => $temp['name'] ];
$temp = &$temp['all_parents'];
} while ($temp !== null);
return array_reverse($retval);
}
You can accumulate the indeterminate-depth data by merging as you recurse the tree. You do not need to introduce any new variables to carry the data while recursing nor do you need to array_reverse() the returned data.
The below technique will prioritize recursion while $structure['all_parents'] is truthy (not null) and cease recursion once it encounters the null all_parents value in the deepest subarray. From the bottom, the id and name elements will be accessed and merged into the empty or accumulated array of row data.
Code: (Demo)
class Recursing
{
public function generateBreadcrumb(array $structure): array
{
return array_merge(
$structure['all_parents']
? $this->generateBreadcrumb($structure['all_parents'])
: [],
[
['id' => $structure['id'], 'name' => $structure['name']]
]
);
}
}
$test = new Recursing;
var_export($test->generateBreadcrumb($arr));
Output:
array (
0 =>
array (
'id' => 1,
'name' => 'Item 1',
),
1 =>
array (
'id' => 2,
'name' => 'Item 2',
),
2 =>
array (
'id' => 3,
'name' => 'Item 3',
),
3 =>
array (
'id' => 4,
'name' => 'Item 4',
),
4 =>
array (
'id' => 5,
'name' => 'Item 5',
),
)
Is there a way to mutate an array using array_reduce in PHP?
I'm trying to do something like this:
Given some ordered list of ids:
$array = [["id" => 1], ["id" => 13], ["id" => 4]];
And a tree that has a subtree matching the corresponding ids:
$tree = [
"id" => 2334,
"children" => [
[
"id" => 111,
"children" => []
],
[
"id" => 1, // <- this is a match
"children" => [
[
"id" => 13, // <- this is a match
"children" => [
[
"id" => 4, // <- this is a match
"children" => []
],
[
"id" => 225893,
"children" => []
],
[
"id" => 225902,
"children" => []
]
]
]
]
]
]
];
How can I mutate the arrays in that subtree?
I'm currently trying to use array_reduce to walk down the tree and mutate it. However, the mutation isn't being applied to the originally passed in $tree.
array_reduce($array, function (&$acc, $item) {
$index = array_search($item['id'], array_column($acc['children'], 'id'));
$acc['children'][$index]['mutated'] = true; // mutation here
return $acc['children'][$index];
}, $tree);
echo "<pre>";
var_dump($tree); // $tree is unchanged here
echo "</pre>";
Why is $tree not mutated after the running above array_reduce?
Is there a way to use foreach in this case?
I think this function will do what you want. It recurses down $tree, looking for id values that are in $array and setting the mutation flag for those children:
function mutate(&$tree, $array) {
if (in_array($tree['id'], array_column($array, 'id'))) {
$tree['mutated'] = true;
}
foreach ($tree['children'] as &$child) {
mutate($child, $array);
}
}
mutate($tree, $array);
var_export($tree);
Output:
array (
'id' => 2334,
'children' => array (
0 => array (
'id' => 111,
'children' => array ( ),
),
1 => array (
'id' => 1,
'children' => array (
0 => array (
'id' => 13,
'children' => array (
0 => array (
'id' => 4,
'children' => array ( ),
'mutated' => true,
),
1 => array (
'id' => 225893,
'children' => array ( ),
),
2 => array (
'id' => 225902,
'children' => array ( ),
),
),
'mutated' => true,
),
),
'mutated' => true,
),
),
)
Demo on 3v4l.org
I want to create dynamic menu with looping an array be 1 object menu. But error occured.
Our code is below it:
$menus = [{"id" => 1, "label" => "content", "parent_id" => 0},{"id" => 2, "label" => "inbox", "id" => 3, "parent_id" => 0}, {"id" => 4, "label" => "item", "parent_id" => 0}];
$sub_menus = [{"id" => 5, "label" => "banner", "parent_id" => 1},{"id" => 6, "label" => "ads", "parent_id" => 1}];
foreach($menus as $row => $value){
$nav[$row] = $value;
foreach($sub_menus as $r => $v) {
if($v['parent_id'] == $value['id']){
$nav[$row]['sub_menu'][$r] = $v;
}
}
}
I get error notif, "Indirect modification of overloaded element of App\Menu has no effect"
Please Help me :)
The code is working. You have a lot of bugs in your arrays.
Fixed:
$menus = [
[
"id" => 1,
"label" => "content",
"parent_id" => 0
],
[
"id" => 2,
"label" => "inbox",
"parent_id" => 0
],
[
"id" => 4,
"label" => "item",
"parent_id" => 0
]
];
$sub_menus = [
[
"id" => 5,
"label" => "banner",
"parent_id" => 1
],
[
"id" => 6,
"label" => "ads",
"parent_id" => 1
]
];
foreach($menus as $row => $value){
$nav[$row] = $value;
foreach($sub_menus as $r => $v) {
if($v['parent_id'] == $value['id']){
$nav[$row]['sub_menu'][$r] = $v;
}
}
}
You have a broken array, something like an incorrect conversion of json to an array;
You can test(execute) it here (working example)
I have an array like this:
$array = array(
0 => array("ordernumber" => "1", "name" => "John", "product" => "laptop", "component" => "memory"),
1 => array("ordernumber" => "1", "name" => "John", "product" => "laptop", "component" => "cpu"),
2 => array("ordernumber" => "1", "name" => "John", "product" => "desktop", "component" => "cpu"),
3 => array("ordernumber" => "2", "name" => "Pete", "product" => "monitor", "component" => "")
);
It contains data from different orders, but as you can see an order can contain multiple purchased products, and each product can contain different 'components'. There's alot of duplicate data in this array, so I would like to turn it into this:
$array = array(
0 => array(
"order" => array(
"ordernumber" => "1", "name" => "John"
),
"products" => array(
0 => array(
"name" => "laptop",
"components" => array("memory", "cpu")
),
1 => array(
"name" => "desktop",
"components" => array("cpu")
)
)
),
1 => array(
"order" => array(
"ordernumber" => "2", "name" => "Pete"
),
"products" => array(
0 => array(
"name" => "monitor",
"components" => array()
)
)
)
);
What would be a good way to do this?
Please use below code to make the solution what you want
<?php
$array = array(
0 => array("ordernumber" => "1", "name" => "John", "product" => "laptop", "component" => "memory"),
1 => array("ordernumber" => "1", "name" => "John", "product" => "laptop", "component" => "cpu"),
2 => array("ordernumber" => "1", "name" => "John", "product" => "desktop", "component" => "cpu"),
3 => array("ordernumber" => "2", "name" => "Pete", "product" => "monitor", "component" => "")
);
$final_array = [];
foreach($array as $k=>$v){
$final_array[$v['ordernumber']]['order']['ordernumber'] = $v['ordernumber'];
$final_array[$v['ordernumber']]['order']['name'] = $v['name'];
$final_array[$v['ordernumber']]['products'][$v['product']]['name'] = $v['product'];
$final_array[$v['ordernumber']]['products'][$v['product']]['components'][] = $v['component'];
}
// You can skip this foreach if there will not metter of KEY of an array in your code!
$final_array = array_values($final_array);
foreach($final_array as $k=>$v){
$final_array[$k]['products'] = array_values($final_array[$k]['products']);
}
echo "<pre>";
print_r($final_array);
?>
its should work!!