How raise an grandchildren array, at the level of children [duplicate] - php

This question already has answers here:
How to Flatten a Multidimensional Array?
(31 answers)
Closed 4 years ago.
I have some problems with parent array that contains multiple children array. It will be necessary that the array children, does not contain themselves other children array, but if it is the case, then to raise these array grandchildren at the level of children.
[0] => Array (
[0] => Array (
[name] => ...
[count] => ...
[url] => ...
[html] => ...
)
[1] => Array (
[name] => ...
[count] => ...
[url] => ...
)
[2] => Array (
[1] => Array (
[name] => ...
[count] => ...
[url] => ...
[html] => ...
)
[2] => Array (
[name] => ...
[count] => ...
[url] => ...
)
)
[3] => Array (
[name] => ...
[count] => ...
[url] => ...
)
[4] => Array (
[name] => ...
[count] => ...
[url] => ...
)
);
In the example array["2"] contain 2 array, I want that the 2 array go up one level and so become brothers and not children.
Can you help me please with the right function ?
function transformMultipleArraysTo1(array $multipleArrays = null)
{
$newArray = array();
if (!isArrayEmpty($multipleArrays)) {
foreach ($multipleArrays as $array) {
if (is_array($array)) {
foreach ($array as $singleArray) {
if (is_array($singleArray)) {
$newArray[] = $singleArray;
} else {
continue 2;
}
}
$newArray[] = $array;
}
}
}
return $newArray;
}
Many thanks

I came up with this solution, not the prettiest, but it works.
function RemoveThoseNastyGrandchildren(array $array = NULL){
foreach ($array as $ckey => $child) {
foreach($child as $gckey => $grandchild){
if(is_array($grandchild)){
$array[] = $grandchild;
array_splice($array[$ckey],$gckey);
}
}
}
$length = count($array);
for($i = 0; $i < $length ; $i++){
if(!empty($array[$i][0]) && is_array($array[$i][0])){
array_splice($array,$i,1);
$i = -1;
$length = count($array);
}
}
//print("<pre>".print_r($array,true)."</pre>"); <---- To print it pretty uncomment
return $array;
}
The First Loop:
will iterate through all relatives, looking for the potential grandchildren. If it finds one of these nasty grandchildren it'll append it to their parents, becoming a grandchildren again.
After that we just have to disensamble the former grandchildrens bodies, and that's what ...
The Second Loop does:
It's iterating through the changed array, looking for empty bodies.
Then return the freshly shaped array familytree and whooosh, you got rid of those GCs.

Related

Sort array values based on parent/child relationship

I am trying to sort an array to ensure that the parent of any item always exists before it in the array. For example:
Array
(
[0] => Array
(
[0] => 207306
[1] => Bob
[2] =>
)
[1] => Array
(
[0] => 199730
[1] => Sam
[2] => 199714
)
[2] => Array
(
[0] => 199728
[1] => Simon
[2] => 207306
)
[3] => Array
(
[0] => 199714
[1] => John
[2] => 207306
)
[4] => Array
(
[0] => 199716
[1] => Tom
[2] => 199718
)
[5] => Array
(
[0] => 199718
[1] => Phillip
[2] => 207306
)
[6] => Array
(
[0] => 199720
[1] => James
[2] => 207306
)
)
In the above array this "fails" as [1][2] (Sam) does not yet exist and nor does [4][2] (Tom).
The correct output would be as, in this case, as both Sam and Tom's parents already exist before they appear in the array:
Array
(
[0] => Array
(
[0] => 207306
[1] => Bob
[2] =>
)
[1] => Array
(
[0] => 199714
[1] => John
[2] => 207306
)
[2] => Array
(
[0] => 199730
[1] => Sam
[2] => 199714
)
[3] => Array
(
[0] => 199728
[1] => Simon
[2] => 207306
)
[4] => Array
(
[0] => 199718
[1] => Phillip
[2] => 207306
)
[5] => Array
(
[0] => 199716
[1] => Tom
[2] => 199718
)
[6] => Array
(
[0] => 199720
[1] => James
[2] => 207306
)
)
I found an answer https://stackoverflow.com/a/12961400/1278201 which was very close but it only seems to go one level deep (i.e. there is only ever one parent) whereas in my case there could be 1 or 10 levels deep in the hierarchy.
How do I sort the array so no value can appear unless its parent already exists before it?
This will trivially order the array (in O(n)) putting first all those with no parent, then these whose parent is already in the array, iteratively, until there's no children having the current element as parent.
# map the children by parent
$parents = ['' => []];
foreach ($array as $val) {
$parents[$val[2]][] = $val;
}
# start with those with no parent
$sorted = $parents[''];
# add the children the current nodes are parent of until the array is empty
foreach ($sorted as &$val) {
if (isset($parents[$val[0]])) {
foreach ($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
This code requires PHP 7, it may not work in some cases under PHP 5. - for PHP 5 compatibility you will have to swap the foreach ($sorted as &$val) with for ($val = reset($sorted); $val; $val = next($sorted)):
# a bit slower loop which works in all versions
for ($val = reset($sorted); $val; $val = next($sorted)) {
if (isset($parents[$val[0]])) {
foreach ($parents[$val[0]] as $next) {
$sorted[] = $next;
}
}
}
Live demo: https://3v4l.org/Uk6Gs
I have two different version for you.
a) Using a "walk the tree" approach with recursion and references to minimize memory consumption
$data = [
[207306,'Bob',''], [199730,'Sam',199714],
[199728,'Simon',207306], [199714,'John',207306],
[199716, 'Tom',199718], [199718,'Phillip',207306],
[199720,'James',207306]
];
$list = [];
generateList($data, '', $list);
var_dump($list);
function generateList($data, $id, &$list) {
foreach($data as $d) {
if($d[2] == $id) {
$list[] = $d; // Child found, add it to list
generateList($data, $d[0], $list); // Now search for childs of this child
}
}
}
b) Using phps built in uusort()function (seems only to work up to php 5.x and not with php7+)
$data = [
[207306,'Bob',''], [199730,'Sam',199714],
[199728,'Simon',207306], [199714,'John',207306],
[199716, 'Tom',199718], [199718,'Phillip',207306],
[199720,'James',207306]
];
usort($data, 'cmp');
var_dump($data);
function cmp($a, $b) {
if($a[2] == '' || $a[0] == $b[2]) return -1; //$a is root element or $b is child of $a
if($b[2] == '' || $b[0] == $a[2]) return 1; //$b is root element or $a is child of $b
return 0; // both elements have no direct relation
}
I checked this works in PHP 5.6 and PHP 7
Sample array:
$array = Array(0 => Array(
0 => 207306,
1 => 'Bob',
2 => '',
),
1 => Array
(
0 => 199730,
1 => 'Sam',
2 => 199714,
),
2 => Array
(
0 => 199728,
1 => 'Simon',
2 => 207306,
),
3 => Array
(
0 => 199714,
1 => 'John',
2 => 207306,
),
4 => Array
(
0 => 199716,
1 => 'Tom',
2 => 199718,
),
5 => Array
(
0 => 199718,
1 => 'Phillip',
2 => 207306,
),
6 => Array
(
0 => 199720,
1 => 'James',
2 => 207306,
),
);
echo "<pre>";
$emp = array();
//form the array with parent and child
foreach ($array as $val) {
$manager = ($val[2] == '') ? 0 : $val[2];
$exist = array_search_key($val[2], $emp);
if ($exist)
$emp[$exist[0]][$val[0]] = $val;
else
//print_R(array_search_key(199714,$emp));
$emp[$manager][$val[0]] = $val;
}
$u_emp = $emp[0];
unset($emp[0]);
//associate the correct child/emp after the manager
foreach ($emp as $k => $val) {
$exist = array_search_key($k, $u_emp);
$pos = array_search($k, array_keys($u_emp));
$u_emp = array_slice($u_emp, 0, $pos+1, true) +
$val +
array_slice($u_emp, $pos-1, count($u_emp) - 1, true);
}
print_R($u_emp); //print the final result
// key search function from the array
function array_search_key($needle_key, $array, $parent = array())
{
foreach ($array AS $key => $value) {
$parent = array();
if ($key == $needle_key)
return $parent;
if (is_array($value)) {
array_push($parent, $key);
if (($result = array_search_key($needle_key, $value, $parent)) !== false)
return $parent;
}
}
return false;
}
Find the below code that might be helpful.So, your output is stored in $sortedarray.
$a=array(array(207306,'Bob',''),
array (199730,'Sam',199714),
array(199728,'Simon',207306),
array(199714,'John',207306),
array(199716,'Tom',199718),
array(199718,'Phillip',207306),
array(199720,'James',207306));
$sortedarray=$a;
foreach($a as $key=>$value){
$checkvalue=$value[2];
$checkkey=$key;
foreach($a as $key2=>$value2){
if($key<$key2){
if ($value2[0]===$checkvalue){
$sortedarray[$key]=$value2;
$sortedarray[$key2]=$value;
}else{
}
}
}
}
print_r($sortedarray);
What about this approach:
Create an empty array result.
Loop over your array and only take the items out of it where [2] is empty and insert them into result.
When this Loop is done you use a foreach-Loop inside a while-loop. With the foreach-Loop you take every item out of your array where [2] is already part of result. And you do this as long as your array contains anything.
$result = array();
$result[''] = 'root';
while(!empty($yourArray)){
foreach($yourArray as $i=>$value){
if(isset($result[$value[2]])){
// use the next line only to show old order
$value['oldIndex'] = $i;
$result[$value[0]] = $value;
unset($yourArray[$i]);
}
}
}
unset($result['']);
PS: You may run into trouble by removing parts of an array while walking over it. If you do so ... try to solve this :)
PPS: Think about a break condition if your array have an unsolved loop or a child without an parent.
you can use your array in variable $arr and use this code it will give you required output.
function check($a, $b) {
return ($a[0] == $b[2]) ? -1 : 1;
}
uasort($arr, 'check');
echo '<pre>';
print_r(array_values($arr));
echo '</pre>';

accessing specific elements of arrays in a foreach loop

I apologize if this is a very simple question - I've read through tons of posts here, but my question is syntactically very hard to search for, so I haven't found an answer yet.
I have a json array that's output from a company's API:
[Result] => Array
(
[cData] => Array
(
[0] => Array
(
[Reqa] => ABCD
[Reqb] =>
[Reqc] => Plus
[dto] => Array
(
[0] => Array
(
[ComID] => 43292392
[Comment] => Dave
)
[1] => Array
(
[ComID] => 43292392
[Comment] => Bob
)
)
[XREFSearchOperation] => Exact
)
[1] => Array
(
[Reqa] => BCDE
[Reqb] =>
[Reqc] => A
[dto] => Array
(
[0] => Array
(
[ComID] => 19331186
[Comment] => Mike
)
[1] => Array
(
[ComID] => 19331186
[Comment] => Roger
)
)
[XREFSearchOperation] => Starts With
)
[2] => Array
(
[Reqa] => QQDT
[Reqb] =>
)
)
)
)
and I'm trying to access the [ComID] and [Comment] elements, if they exist, inside of a foreach loop and assign it to the variable $y
So far I have:
foreach ($json['Result']['cData']['dto'] as $i) {
$y = "{$i['ComID']}|{$i['Comment']}";
}
but this gives me zero results. I understand WHY, because in between ['cData'] and ['dto'] are [0], [1], [2] etc.. elements, and I don't know how to add a qualifier for those into the loop.
Update
This code works for most of the json response:
foreach ($json['Result']['cData'] as $i) {
if(array_key_exists('dto', $i)) {
foreach ($i['dto'] as $j) {
$y = "{$j['ComID']}|{$j['Comment']}";
} else {
}
}
However - I'm having one more small issue. If there are multiple [dto] responses, you'll have [dto][0][ComID] then [dto][1][ComID] and [dto][2][ComID], (like in the example above,) but if there's only ONE response, you'll have [dto][ComID] as there's no need for that middle array.
I tried writing if(array_key_exists('dto[0]' to execute one, then an else statement in the event dto[0] doesn't exist, but that didn't work. I need a way of NOT executing a foreach loop if there is no array underneath it to "foreachicize". Is there an if/else statement I can write to accommodate this?
Probably need a nested foreach:
foreach ($json['Result']['cData'] as $i) {
foreach($i['dto'] as $j) {
$y[] = "{$j['ComID']}|{$j['Comment']}"; //need an array here
}
}
For the update to the question. Check if $i['dto'][0] exists:
foreach ($json['Result']['cData'] as $i) {
if(isset($i['dto'][0]))) {
foreach($i['dto'] as $j) {
$y[] = "{$j['ComID']}|{$j['Comment']}";
}
} else {
$y[] = "{$j['ComID']}|{$j['Comment']}";
}
}
There might be a better way but I'm headed out.
Another approach:
foreach($json['Result']['cData'] as $cData)
{
foreach($cData['dto'] as $dto)
{
if(array_key_exists('ComID', $dto) && array_key_exists('Comment', $dto))
{
$y = "{$dto['ComID']}|{$dto['Comment']}";
}
}
}

values not push on recursive array

Let me explain my problem. I try to generate an array of categories.
Here is my function.
private function recursiveCategoriesTree($id_parent, $level, $categories)
{
$tree = array();
foreach($categories as $categorie)
{
if($id_parent == $categorie['id_parent'])
{
$tree[$level][] = $categorie;
$this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
}
}
return $tree;
}
When I trace the loop with echo, everything seems to work, all the parent categories and girls are covered, but are not pushed into the array.
Here is the print_r of my categories
array(
[0] => Array
(
[id_category] => 4
[name] => Pièces détachées
[id_parent] => 1
)
[1] => Array
(
[id_category] => 5
[name] => Excavateur
[id_parent] => 4
)
[2] => Array
(
[id_category] => 6
[name] => série 100
[id_parent] => 5
)
[3] => Array
(
[id_category] => 7
[name] => above
[id_parent] => 6
)
[4] => Array
(
[id_category] => 8
[name] => système hydraulique
[id_parent] => 7
)
[5] => Array
(
[id_category] => 9
[name] => série 200
[id_parent] => 5
)
[6] => Array
(
[id_category] => 10
[name] => thru
[id_parent] => 6
)
[7] => Array
(
[id_category] => 11
[name] => Compaction
[id_parent] => 4
)
)
Here is the result of print_r generated
Array(
[0] => Array
(
[0] => Array
(
[id_category] => 5
[name] => Excavateur
[id_parent] => 4
)
[1] => Array
(
[id_category] => 11
[name] => Compaction
[id_parent] => 4
)
)
)
I call my function like that
$tree = $this->recursiveCategoriesTree(4, 0, $categories)
What is the problem ? thank you =)
Either get the return value from your recursive call and push that onto the array, or make a private property of the class called tree and push values onto that instead. You are not passing the variable $tree across recursive function calls.
E.g. if you do this it will work: (EDIT: Fixed... [again])
private $catTree = array();
private $catStartLevel = FALSE;
private function recursiveCategoriesTree($id_parent, $level, $categories) {
if ($this->catStartLevel !== FALSE) $this->catStartLevel = $level;
foreach($categories as $categorie) {
if($id_parent == $categorie['id_parent']) {
$this->catTree[$level][] = $categorie;
$this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
}
}
if ($this->catStartLevel === $level) {
$tree = $this->catTree;
$this->catTree = array();
$this->catStartLevel = FALSE;
}
return $tree;
}
...however this is not great, because you now have a 'pointless' private property in your class. You would be better to change you array structure, and catch the return values from $this->recursiveCategoriesTree()...
EDIT
Thinking about it, if you really want the array in that structure, you would probably be better to pass the variable to be populated with the array by reference:
private function recursiveCategoriesTree($id_parent, $level, $categories, &$tree) {
foreach($categories as $categorie) {
if($id_parent == $categorie['id_parent']) {
$tree[$level][] = $categorie;
$this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories, $tree);
}
}
}
...and then you would call it like this...
$myTree = array();
$obj->recursiveCategoriesTree($id_parent, $level, $categories, $myTree);
print_r($myTree);
recursiveCategoriesTree() returns the $tree, but you're not doing anything with that return value when you're calling the method recursively. You're only storing the $tree returned from the initial call to the method.
Perhaps you want something like this?
$categorie['children'] = $this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
You should first fetch all the childs of a category, and add them to its array, before appending the category to the tree. Kind of like this:
foreach($categories as $categorie)
{
$categorie['childs'] = $this->recursiveCategoriesTree($categorie['id_category'], ($level+1), $categories);
$tree[$level][] = $categorie;
}

PHP: Iterating through array?

I want a function that
searches through my array, and
returns all the
children to a specific node. What is
the most appropriate way to do this?
Will recursion be necessary in this case?
I have previously constructed a few quite complex functions that iterates with or without the help of recursion through multi-dimensional arrays and re-arranging them, but this problem makes me completely stuck and I can't just get my head around it...
Here's my array:
Array
(
[1] => Array (
[id] => 1
[parent] => 0
)
[2] => Array (
[id] => 2
[parent] => 1
)
[3] => Array (
[id] => 3
[parent] => 2
)
)
UPDATE:
The output which I want to get. Sorry for the bad example, but I'll blame it on lack of knowledge on how to format the stuff I need to do :)
function getAllChildren($id) {
// Psuedocode
return $array;
}
getAllChildren(1); // Outputs the following:
Array
(
[2] => Array (
[id] => 2
[parent] => 1
)
[3] => Array (
[id] => 3
[parent] => 2
)
)
$nodes = array( 1 => array ( 'id' => 1,
'parent' => 0
),
2 => array ( 'id' => 2,
'parent' => 1
),
3 => array ( 'id' => 3,
'parent' => 2
)
);
function searchItem($needle,$haystack) {
$nodes = array();
foreach ($haystack as $key => $item) {
if ($item['parent'] == $needle) {
$nodes[$key] = $item;
$nodes = $nodes + searchItem($item['id'],$haystack);
}
}
return $nodes;
}
$result = searchItem('1',$nodes);
echo '<pre>';
var_dump($result);
echo '</pre>';
Non-recursive version of the searchItem() function:
function searchItem($needle,$haystack) {
$nodes = array();
foreach ($haystack as $key => $item) {
if (($item['parent'] == $needle) || array_key_exists($item['parent'],$nodes)) {
$nodes[$key] = $item;
}
}
return $nodes;
}
(assumes ordering of the parents/children, so a child node isn't included in the array unless the parent is already there)
<?php
function searchItem($needle)
{
foreach ($data as $key => $item)
{
if ($item['id'] == $needle)
{
return $key;
}
}
return null;
}
?>
Check out the array_walk_recursive() function in PHP:
http://www.php.net/manual/en/function.array-walk-recursive.php

php - recreate array?

I've "inherited" some data, which I'm trying to clean up. The array is from a database which, apparently, had no keys.
The array itself, is pretty long, so I'm simplifying things for this post...
[0] => Array
(
[id] => 2
[uid] => 130
[eid] => 8
[ename] => Standard
[eaction] => Check
)
[1] => Array
(
[id] => 2
[uid] => 110
[eid] => 8
[ename] => Standard
[eaction] => Check
)
[2] => Array
(
[id] => 2
[uid] => 200
[eid] => 8
[ename] => Standard
[eaction] => Check
)
I'm trying to shift things around so the array is multidimensional and is grouped by ename:
[0] => Array
(
[Standard] => Array
(
[id] => 2
[uid] => 130
[eid] => 8
[eaction] => Check
)
)
[0] => Array
(
[Standard] => Array
(
[id] => 2
[uid] => 130
[eid] => 8
[eaction] => Check
)
)
[0] => Array
(
[Standard] => Array
(
[id] => 2
[uid] => 130
[eid] => 8
[eaction] => Check
)
)
Anyone know how to do something like this?
You can use usort() to sort an array by a user-defined function. That function could compare the ename fields. Then it's just a simple transformation. Like:
usort($array, 'cmp_ename');
function cmp_ename($a, $b) {
return strcmp($a['ename'], $b['ename']);
}
and then:
$output = array();
foreach ($array as $v) {
$ename = $v['ename'];
unset($v['ename']);
$output[] = array($ename => $v);
}
$outputarray = array();
foreach($inputarray as $value) {
$outputarray[] = array($value['ename'] => $value);
}
would accomplish what your examples seem to indicate (aside from the fact that your 'result' example has multiple things all with key 0... which isn't valid. I'm assuming you meant to number them 0,1,2 et cetera). However, I have to wonder what benefit you're getting from this, since all it appears to be doing is adding another dimension that serves no purpose. Perhaps you could clarify your example if there are other things to take into account?
$outputarray = array();
foreach($inputarray as &$value) {
$outputarray[][$value['ename']] = $value;
unset($value['ename']);
} unset($value);
I'm guessing that this is what you're asking for:
function array_group_by($input, $field) {
$out = array();
foreach ($input as $row) {
if (!isset($out[$row[$field]])) {
$out[$row[$field]] = array();
}
$out[$row[$field]][] = $row;
}
return $out;
}
And usage:
var_dump(array_group_by($input, 'ename'));
philfreo was right but he was also off a little. with his code every time you encounter an array element with an ['ename'] the same as one you've already gone through it will overwrite the data from the previous element with the same ['ename']
you might want to do something like this:
$output = array();
foreach ($YOURARRAY as $value) {
$output[$value['ename']][] = $value;
}
var_dump($output); // to check out what you get

Categories