Re-arrange array elements if they meet a condition - php

The array:
array(
'354' => array(
'parent' => 0
),
'370' => array(
'parent' => 0
),
'420' => array(
'parent' => 354
),
)
How can I move all elements that have the 'parent' value != 0, just after the element to which the key is the same as that parent value?
For example, the element with the 420 keys above needs to go after the element with the 354 key...

That's impossible in the general case. In your example it would work.
But here's another example:
Number | Parent
10 | 1
11 | 1
100 | 10
101 | 10
102 | 10
1000 | 100
1001 | 100
So you want that all the three rows 100, 101, 102 come directly after the row 10, which is impossible.
And between row 100 and 101 you probably want the rows 1000 and 1001, since their parent is 100.
[Update]
So there remain these questions:
Do you just need the nodes ordered so that each node is defined somewhere before it is used as a parent?
Do the direct children have to follow their parent directly, oder may there be some other nodes in between?
Is the parent ID always smaller than the node ID?

Maybe you're looking at a way to make a flat array have some sort of hierarchie?
This is a very simple case which will do that asuming your children always have a higher id than their parent.
<?php
$array = array(
'354' => array(
'parent' => 0
),
'370' => array(
'parent' => 0
),
'420' => array(
'parent' => 354
),
'550' => array(
'parent' => 420
),
);
/**
*
* This function will convert a flat array with elements to the proper hierarchy structure
* #param array $array
*/
function hierarchy(&$array) {
arsort($array);
foreach($array as $key => $value)
{
if($value['parent'])
{
$array[$value['parent']]['children'][$key] = $value;
unset($array[$key]);
}
}
asort($array);
return $array;
}
(print '<pre>' . print_r(hierarchy($array), true) . '</pre>');
Heres a testcase:
http://codepad.org/RT51uOfn

Related

Count all Children of Nodes in a Hierarchical (Tree) Data Structure in MySQL using PHP

I want to count the number of all children in any level of a Tree Structure. The Tree isn't binary, so a node can have more than 2 children.
Right now I have created a recursive function that does that job, but it is really slow. I need a faster way, but I can't do it in any other way.
So, let's say we have that table:
NodeID | ParentID
-------------------
1 | 0
2 | 1
3 | 1
4 | 2
5 | 1
6 | 2
7 | 3
8 | 5
9 | 6
10 | 8
11 | 6
11 | 6
• 1
• 2
• 4
• 6
• 9
• 11
• 12
• 3
• 7
• 5
• 8
• 10
So, if I want to get the children number of node 1, the number should be 11 instead of 3. The same thing for Node 2. The number should be 5 instead of 2.
This is the Recursive function I have created in PHP, but it is SLOW:
function count_all_nodes($connection, $element_id, $elements=false, $i=0) {
if (!$elements) {
$result = mysqli($connection, "SELECT `node_id`, `parent_id` FROM `elements`");
while ($row = mysqli_fetch_array($result)) {
$sub_elements["node_id"] = $row["node_id"];
$sub_elements["parent_id"] = $row["parent_id"];
$elements[] = $sub_elements;
}
}
foreach ($elements as $element) {
if ($element["parent_id"] == $element_id) {
$i++;
$i += count_all_nodes($connection, $element_id, $elements);
}
}
return $i;
}
Is there any way I can avoid this recursive function?
Thank you!
I think hierarchical data structure (tree) is synonymous of recursion.
I implement two recursive function in php that maybe are faster than your code, because I only execute one MySql query to get the tree information, not one in each call to the recursive function like you.
Example data:
The root node (tree's first node) doesn't have a father, I indicated this setting into its father field '0-A' (this node doesn't exist), but you can put null instead.
To get the tree information I execute this query:
SELECT node, father
FROM tree
ORDER BY node;
I get something like this in php:
$nodes = array ( 0 => array ( 'node' => '1-A', 'father' => '0-A', ), 1 => array ( 'node' => '2-A', 'father' => '1-A', ), 2 => array ( 'node' => '3-A', 'father' => '1-A', ), 3 => array ( 'node' => '4-A', 'father' => '2-A', ), 4 => array ( 'node' => '5-A', 'father' => '2-A', ), 5 => array ( 'node' => '6-A', 'father' => '3-A', ), 6 => array ( 'node' => '7-A', 'father' => '3-A', ), 7 => array ( 'node' => '9-A', 'father' => '6-A', ), 8 => array ( 'node' => '10-A', 'father' => '7-A', ), 9 => array ( 'node' => '8-A', 'father' => '7-A', ), 10 => array ( 'node' => '11-A', 'father' => '10-A', ), )
This is my php code:
/**
* Count all Children of each node in a Hierarchical Data Structure (Tree).
*
* #param array(idNode => idNodeFather) $nodes
* #param string $actualNode
* #param array(idDode => totalChildrens) $result
*/
function countChildrensNode($nodes, $actualNode, &$result) {
foreach($nodes as $node => $father) {
if($actualNode == $father) {
$result[$father]++;
countChildrensNode($nodes, $node, $result);
incrementActualNodeAncestor($nodes, $father, $result);
}
}
}
/**
* Increment by one to all actual node's ancestors.
*
* #param array(idNode => idNodeFather) $nodes
* #param string $actualNode
* #param array(idDode => totalChildrens) $result
*/
function incrementActualNodeAncestor($nodes, $actualNode, &$result) {
foreach($nodes as $node => $father) {
if($actualNode == $node) {
if($father != '0-A' && ! is_null($father)) {
$result[$father]++;
incrementActualNodeAncestor($nodes, $father, $result);
}
}
}
}
//$nodes is the array return by my query.
$nodesfathers = array_combine (array_column($nodes, "node"), array_column($nodes, "father"));
$res = array_fill_keys(array_column($nodes, "node"), 0);
countChildrensNode($nodesfathers, array_keys($nodesfathers)[0], $res);
countChildrensNode function search forward (increment by one the actual node's father and call incrementActualNodeAncestor the second recursive function) and incrementActualNodeAncestor look for backward (and increment by one each ancestor of the actual node).
The result is in $res var:
'1-A'
10
'2-A'
2
'3-A'
6
'4-A'
0
'5-A'
0
'6-A'
1
'7-A'
3
'9-A'
0
'10-A'
1
'8-A'
0
'11-A'
0

PHP: one dimensional array to multidimensional array with element multiplication

Is there an easy way to transform a one dimensional array into multidimensional array and along with that add a certain element to the newly created sub-arrays?
The countrycode always has 2 digits and appears only once before a set of locations. That's the element that I'd like to duplicate and add it to every subarray as shown below.
Thanks in advance!
That's what I have:
0 => AT
1 => Vienna-S03-I01
2 => 28 Users
3 => Vienna-S03-I02
4 => 25 Users
5 => Vienna-S03-I03
6 => 24 Users
7 => AU
8 => Sydney-S01-I01
9 => 45 Users
10 => BE
11 => Brussels-S01-I01
12 => 30 Users
13 => Brussels-S01-I02
14 => 37 Users
That's what I'd like to have:
0 =>
0 => AT
1 => Vienna-S03-I01
2 => 28 Users
1 =>
0 => AT
1 => Vienna-S03-I02
2 => 25 Users
2 =>
0 => AT
1 => Vienna-S03-I03
2 => 24 Users
3 =>
0 => AU
1 => Sydney-S01-I01
2 => 45 Users
4 =>
0 => BE
1 => Brussels-S01-I01
2 => 30 Users
5 =>
0 => BE
1 => Brussels-S01-I02
2 => 37 Users
If there is a guarantee that array will always follow rule you demonstrated, then code is below. Otherwise few condition check should be added there to make sure that we have proper value type in every given $item.
$array = ['AT',
'Vienna-S03-I01',
'28 Users',
'Vienna-S03-I02',
'25 Users',
'Vienna-S03-I03',
'24 Users',
'AU',
'Sydney-S01-I01',
'45 Users',
'BE',
'Brussels-S01-I01',
'30 Users',
'Brussels-S01-I02',
'37 Users'];
$code='none';
$result=[];
$resIndex=-1;
$swing=false;
foreach($array as $item){
if (strlen($item)===2){
$code=$item;
}else{
if ($swing===false){
$resIndex++;
$result[$resIndex][]=$code;
}
$result[$resIndex][]=$item;
$swing=!$swing;
}
}
print_r($result);
?>
This is a very light method in terms of function calls and variables. isset() is a very swift function call, so there will be minimal drag there. The snippet is condensed by declaring multiple variables in a single line ($result[++$i][]=$k=$v;) and by incrementing the counter (++$i) inside of the result array declarations.
Code: (Demo)
$array = ['AT',
'Vienna-S03-I01',
'28 Users',
'Vienna-S03-I02',
'25 Users',
'Vienna-S03-I03',
'24 Users',
'AU',
'Sydney-S01-I01',
'45 Users',
'BE',
'Brussels-S01-I01',
'30 Users',
'Brussels-S01-I02',
'37 Users'];
$i=-1;
foreach($array as $v){
if(strlen($v)==2){ // check for new Country ID
$result[++$i][]=$k=$v; // preserve Country ID as $k, store as a new batch
}else{
if(isset($result[$i][2])){ // when three elements in current batch, start new...
$result[++$i][]=$k; // increment counter and store preserved Country ID
}
$result[$i][]=$v; // add $v to batch
}
}
var_export($result);
p.s. as a matter of further micro-optimization, you could swap out strlen() for another isset() call -- but that is a bit less intuitive:
$i=-1;
foreach($array as $v){
if(!isset($v[2])){ // check for existence of character at offset 2
$result[++$i][]=$k=$v;
}else{
if(isset($result[$i][2])){
$result[++$i][]=$k;
}
$result[$i][]=$v;
}
}
This is a functional approach that doesn't need to iterate for each element in the array. I just love array_splice() and its fantastic dual action of extracting elements and shortening the input array:
// array_splice modifies the original array (shortens it) and returns the removed elements
while($array){
if(strlen($array[0])==2){ // if the first value is a Country id
$id=$array[0]; // preserve id
$result[]=array_splice($array,0,3); // cut away and preserve first three elements
}else{
$result[]=[$id]+array_splice($array,0,2); // store id and first two elements
}
}
var_export($result);
...and finally, my DRYest method:
while($array){
if(strlen($array[0])==2){ // if the first value is a Country id
$id=array_splice($array,0,1);
}
$result[]=array_merge($id,array_splice($array,0,2)); // store preserved id and two elements
}
var_export($result);

MySQL entries with self parent_id into sorted cascade array

I need help with an application I'm creating.
I have a table, looks kind like this:
+----+-----------+---------+
| id | parent_id | name |
+----+-----------+---------+
| 1 | null | test |
+----+-----------+---------+
| 2 | null | test2 |
+----+-----------+---------+
| 4 | 1 | test3 |
+----+-----------+---------+
| 5 | 2 | test4 |
+----+-----------+---------+
And now, I get all the data in one array. I would like to get kinda this structure (php array as an cascade):
array(
0 => array(
'id' => 1,
'parent_id' => null,
'name' => 'test',
'children' => array(
'id' => 4,
'parent_id' => 1,
'name' => 'test3'
)
),
1 => array(
'id' => 2,
'parent_id' => null,
'name' => 'test2',
'children' => array(
'id' => 5,
'parent_id' => 2,
'name' => 'test4'
)
)
)
So there will every entry with a "parent_id=null" be a parent, and every entry with an id in "parent_id" will be in a child array.
I started it like this:
$newArray = array();
foreach($names as $name) {
if($name['parent_id'] == null || $name['parent_id'] == 0) {
// entry is parent
$newArray[$name['id']] = $name['name'];
} else {
// entry is child
}
}
But here is also my end, I don't know how to do that. I think i have to use some kind of recursive loop function, but I don't know how to start.
Would be awesome if somebody could help me.
Kind regards,
Matt.
You can use a recursive function like this (I only added the code which is relevant for understanding):
function get_children($parentId) {
$array = array();
//Load/Find $children
foreach($children as $child) {
$array[] = array(
'id' => $child->id,
'name' => 'YourName',
'children' => get_children($child->id)
);
}
return $array;
}
If you save the data in such an array, it isn't necessary to save the parent_id, because you can get it by searching for the parent elements id.

PHP array sort mutli-dimensional

I have an array structured like so (this is the way the CSV formats it):
Array(
0 => Array(
0 => person1
1 => person2
2 => person3
//all the way to 9
),
1 => Array(
0 => id belonging to person 1
1 => id belonging to person 2
2 => id belonging to person 3
),
2 => Array(
0 => id belonging to person 1
1 => id belonging to person 2
2 => id belonging to person 3
),
//all the way to 20
)
I'm trying to sort a new array (of arrays), with each index being the value correspondent to the key in the 0 index above. i.e., person1 points to an array with all ids from the arrays 1-20 outside.
In each of the arrays after the index 0, it contains 20 ids, 0 belongs to the key 0 in the first array.
The structure I'm trying to achieve is shown below:
Array(
[person1] => Array(
id belonging to person 1
id belonging to person 1
id belonging to person 1
),
[person2] => Array(
id belonging to person 2
id belonging to person 2
id belonging to person 2
),
[person3] => Array(
id belonging to person 3
id belonging to person 3
id belonging to person 3
),
)
My attempt so far has worked, however, I had to hard code some of the indexes. What's the best solution to achieve the desired structure?
I'm a bit unsure if this is what you are looking for...
<?php
$arr = Array(
0 => Array(
0 => "person1",
1 => "person2",
2 => "person3"
//all the way to 9
),
1 => Array(
0 => "id belonging to person 1",
1 => "id belonging to person 2",
2 => "id belonging to person 3"
),
2 => Array(
0 => "id belonging to person 1",
1 => "id belonging to person 2",
2 => "id belonging to person 3"
)
);
foreach($arr[0] AS $id=>$name)
{
$ids[$id] = $name;
}
foreach(array_slice($arr,1) AS $persons)
{
foreach($persons AS $id=>$person)
{
// make sure to check if $ids[$id] exist and handle it as you like.
// if(isset($ids[$id]))
$people[$ids[$id]][] = $person;
}
}
print_r($people);
?>
result:
Array
(
[person1] => Array
(
[0] => id belonging to person 1
[1] => id belonging to person 1
)
[person2] => Array
(
[0] => id belonging to person 2
[1] => id belonging to person 2
)
[person3] => Array
(
[0] => id belonging to person 3
[1] => id belonging to person 3
)
)
EDIT: Should be noted I'm not making any checks on if the person's id exist in the $ids array, and neither if $people are ever set.

PHP, mysql_fetch_array and decompose an array into multiple arrays

I have a database table (QUEUE) like this:
queueId phoneNumber
1 340 000
1 340 111 1
1 340 222
2 332 000
2 332 111
3 421 000
3 421 111
3 421 222
I use this query:
SELECT * FROM queue ORDER BY queueId
and this php code:
while ($rowQueue = mysql_fetch_array($resultQueryQueue, MYSQL_ASSOC)) {
$queue[] = array(
'queueId' => $rowQueue['queueId'],
'phoneNumber' => $rowQueue['phoneNumber']
);
}
result is a array with 8 arrays (beacuse record are 8).
I would like to get an array that contains arrays as there are so many keys. In my example I would like to get 3 arrays, the first key 1, the second and the third with key 2 and key 3.
How can I make PHP? Is there any function that can help me?
I think of two way to do that (off course there will be few other).
One you can do with mysql query IF "queryId" is a foreign key you can get all the phoneNumber associated with specific queryId
SECOND: i am Guessing you will get a result from query is like this
$arr = array(
array(
'queryid' => 1,
'phonenumber'=>350000
),
array(
'queryid' => 1,
'phonenumber'=>350000
),
array(
'queryid' => 2,
'phonenumber'=>340001
),
array(
'queryid' => 2,
'phonenumber'=>340002
)
);
You can sort this by
$ret = array();
foreach ($arr as $k => $v)
{
$ret [$v['queryid']][]=$v['phonenumber'];
}
var_dump($ret);
key of this array will be 'queryid' and will have array of phonenumber related to that key
And also consider removing space from phone number.
Let me know if it worked. thanks

Categories