I have a flat structure that I need to convert to a nested tree-like structure. This is different from other Stack Overflow questions because the children can repeat (i.e. can have the same questionId and parentId). I have attempted to solve this with branching recursion with no luck.
Input (flat array):
[
[
'questionId' => 1,
'name' => 'albumName',
'parentId' => 0,
'answer' => "Name of album",
],
[
'questionId' => 2,
'name' => 'albumGenre',
'parentId' => 0,
'answer' => "album genre",
],
[
'questionId' => 3,
'name' => 'trackStart',
'parentId' => 0,
],
[
'questionId' => 4,
'name' => 'trackName',
'parentId' => 3,
'answer' => "Track One",
],
[
'questionId' => 5,
'name' => 'trackEnd',
'parentId' => 3,
],
[
'questionId' => 3,
'name' => 'trackStart',
'parentId' => 0,
],
[
'questionId' => 4,
'name' => 'trackName',
'parentId' => 3,
'answer' => "Track Two",
],
[
'questionId' => 6,
'name' => 'artistStart',
'parentId' => 3,
],
[
'questionId' => 7,
'name' => 'artistName',
'parentId' => 6,
'answer' => "Artist Name",
],
[
'questionId' => 8,
'name' => 'artistEnd',
'parentId' => 6,
],
[
'questionId' => 5,
'name' => 'trackEnd',
'parentId' => 3,
],
[
'questionId' => 9,
'name' => 'albumDate',
'parentId' => 0,
'answer' => "album Date",
]
]
Desired Output (nested array):
[
'albumName' => 'Album Name',
'albumGenre' => 'Album Genre',
'trackStart' => [
[
'trackName' => 'Track One'
],
[
'trackName' => 'Track Two',
'artistStart' => [
[
'artistName' => 'Artist Name'
]
]
]
],
'albumDate' => 'album Date'
]
You can solve this using Reference Pointers:
$newArray = array();
$pointer[] = &$newArray;
foreach($arr as $ar) {
if(stristr($ar['name'], "start")) { // Start
$pointer[] = &$pointer[count($pointer)-1][$ar['name']][];
} else if(stristr($ar['name'], "end")) { // End
array_pop($pointer);
} else {
$pointer[count($pointer)-1][$ar['name']] = $ar['answer'];
}
}
To make it faster you can use stripos($ar['name'], "start") !== false;
Related
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);
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',
],
],
]
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
I Have this array and i want to get all parent have a balance :
this array is multi rows note that sub-array is not static could be more
Thanks
$array_content = [
'id'=> 4,
'Parent' => [
[
'id' => 54,
'Parent' => [
[
'id' => 324,
'KUI' => 'ABC',
'figure' => 'Tira',
'Parent'=> []
],
[
'id' => 52355,
'lft' => 'LEFT',
'Parent' => [
[
'id' => 4,
'Parent' => [
[
'id' => 234,
'ui' => 'UITed',
'Parent'=> ['Balance'=>450.3]
]
]
],
[
'id' => 76,
'ui' => 'some value',
'Parent'=> []
]
],
]
],
],
[
'id' => 23,
'title' => 'ABC',
'Parent' => [
],
]
]
];
The output that i need to see is this balance by the id =234 from parent:
'Balance'=>450.3
You can also, instead of iterating the array, make it Json and use substr and strpos (or regex) to find the value.
$json = json_encode($array_content);
$pos = strpos($json, "Balance\":")+9; // +9 for Balance": is 9 characters
Echo substr($json, $pos, strpos($json, "}", $pos)-$pos); // 450.3
https://3v4l.org/cojXe
Here i found the answer Thanks everyone :
$array_content = [
'id'=> 4,
'Parent' => [
[
'id' => 54,
'Parent' => [
[
'id' => 324,
'KUI' => 'ABC',
'figure' => 'Tira',
'Parent'=> []
],
[
'id' => 52355,
'lft' => 'LEFT',
'Parent' => [
[
'id' => 4,
'Parent' => [
[
'id' => 234,
'ui' => 'UITed',
'Parent'=> ['Balance'=>450.3]
]
]
],
[
'id' => 76,
'ui' => 'some value',
'Parent'=> []
]
],
]
],
],
[
'id' => 23,
'title' => 'ABC',
'Parent' => [
],
]
]
];
function search($arr,$id)
{
if(gettype($arr) == 'array')
foreach($arr as $key =>$list)
{
if(gettype($list) == 'array'){
if(isset($list['id']))
{
if($list['id'] ==$id)
print_r($list['Parent']);
}
search($list,$id);
}
}
}
foreach($array_content as $key)
{
search($key,234);
}
I'm stuck with transforming flat array to multidimensional tree like. I have already done it, but I used references, which creates another set of problems down the line, so I need to do this without references.
Input array:
Array
[
1 =>[
'content_id' => 1,
'sort_order' => 1,
'level' => 1
],
2 =>[
'content_id' => 7,
'sort_order' => 2,
'level' => 2
],
3 =>[
'content_id' => 4,
'sort_order' => 3,
'level' => 2
],
4 =>[
'content_id' => 2,
'sort_order' => 4,
'level' => 3
],
5 =>[
'content_id' => 3,
'sort_order' => 5,
'level' => 1
],
6 =>[
'content_id' => 6,
'sort_order' => 6,
'level' => 1
],
7 =>[
'content_id' => 5,
'sort_order' => 7,
'level' => 2
]
]
Output array:
1 => [
'id' = 1,
'visited' = 0,
'children' => [
2 => [
'id' => 7,
'visited' => 0,
'children' => []
],
3 => [
'id' => 4,
'visited' => 0,
'children' => [
4 => [
'id' = 2,
'visited' = 0,
'children' => []
]
]
],
5 => [
'id' => 3,
'visited' => 0,
'children' => []
],
6 => [
'id' => 6,
'visited' => 0,
'children' => [
7 => [
'id' => 5,
'visited' => 0,
'children => []
]
]
]
Any idea how to tackle a problem like this without having direct parent relation set? I can use recursion, but references are problem.
Oooh, this kind of problems do tickle my fancy. So, here is my solution:
<?php
$origArray = array(
array('content_id' => 1, 'sort_order' => 1, 'level' => 1),
array('content_id' => 7, 'sort_order' => 2, 'level' => 2),
array('content_id' => 4, 'sort_order' => 3, 'level' => 2),
array('content_id' => 2, 'sort_order' => 4, 'level' => 3),
array('content_id' => 3, 'sort_order' => 5, 'level' => 1),
array('content_id' => 6, 'sort_order' => 6, 'level' => 1),
array('content_id' => 5, 'sort_order' => 7, 'level' => 2),
);
function sortByOrder($a, $b) {
if ($a['sort_order'] == $b['sort_order']) {
return 0;
}
return ($a['sort_order'] < $b['sort_order']) ? -1 : 1;
}
function createHierarchicalArray($arr) {
$result = array();
foreach ($arr as $el) {
$result = insertArrayElement($result, $el['content_id'], $el['level']);
}
return $result;
}
function insertArrayElement($array, $id, $level, $currentLevel = 1) {
if ($level > $currentLevel) {
$ids = array_keys($array);
$currentId = end($ids);
if (!isset($array[$currentId]['children'])) {
$array[$currentId]['children'] = array();
}
$array[$currentId]['children'] = insertArrayElement($array[$currentId]['children'], $id, $level, $currentLevel + 1);
} else {
$array[$id] = array();
}
return $array;
}
// Could do without this, if the array is already sorted. Otherwise it's a necessary step.
uasort($origArray, 'sortByOrder');
$result = createHierarchicalArray($origArray);
var_dump($result);
Edit: Changed the code to incorporate the changes in the question.