With a table on my database that stores items of a menu, where every item has an ID, a NAME, and a FATHER ID, I need to arrange it and get a tree-like structure of multiple levels. What I need is an array with the top level menus, then every element with his 'childs' array that contains the sub menus, and this sub menus with their 'childs' array containing their respective sub sub menus an so for. English is no my native language so bear with me :)
An example for better understanding.
I have the following menu in array form:
1- System
2- Profile
3- Account
4- Info
5- Security
6- Logout
With the following array:
$array = array(
array('id' => 1, 'item'=>'System', 'id_father' => null),
array('id' => 2, 'item'=>'Profile', 'id_father' => 1),
array('id' => 3, 'item'=>'Account', 'id_father' => 2),
array('id' => 4, 'item'=>'Info', 'id_father' => 3),
array('id' => 5, 'item'=>'Security', 'id_father' => 3),
array('id' => 6, 'item'=>'Logout', 'id_father' => 1)
);
How can I get the following ? :
array(
array('id' => 1, 'item'=>'System', 'id_father' => null,
'childs' => array(
array('id' => 2, 'item'=>'Profile', 'id_father' => 1),
array('id' => 3, 'item'=>'Account', 'id_father' => 2,
'childs' => array(
array('id' => 4, 'item'=>'Info', 'id_father' => 3),
array('id' => 5, 'item'=>'Security', 'id_father' => 3)
),
),
),
),
array('id' => 6, 'item'=>'Logout', 'id_father' => 1)
);
Change $array to :
$array = array(
array('id' => 1, 'item'=>'System', 'id_father' => null),
array('id' => 2, 'item'=>'Profile', 'id_father' => 1),
array('id' => 3, 'item'=>'Account', 'id_father' => 1), // set id_father = 1
array('id' => 4, 'item'=>'Info', 'id_father' => 3),
array('id' => 5, 'item'=>'Security', 'id_father' => 3),
array('id' => 6, 'item'=>'Logout', 'id_father' => null) // edited to set id_father = null
);
Do it:
function tree( $ar, $pid = null ) {
$op = array();
foreach( $ar as $item ) {
if( $item['id_father'] == $pid ) {
$op[$item['id']] = array(
'item' => $item['item'],
'id_father' => $item['id_father'],
'id' => $item['id']
);
// using recursion
$children = tree( $ar, $item['id'] );
if( $children ) {
$op[$item['id']]['childs'] = $children;
}
}
}
return $op;
}
$tree = tree($array);
echo '<pre>';
print_r( $tree);
echo '</pre>';
// OUTPUT
Array
(
[1] => Array
(
[item] => System
[id_father] =>
[id] => 1
[childs] => Array
(
[2] => Array
(
[item] => Profile
[id_father] => 1
[id] => 2
)
[3] => Array
(
[item] => Account
[id_father] => 1
[id] => 3
[childs] => Array
(
[4] => Array
(
[item] => Info
[id_father] => 3
[id] => 4
)
[5] => Array
(
[item] => Security
[id_father] => 3
[id] => 5
)
)
)
)
)
[6] => Array
(
[item] => Logout
[id_father] =>
[id] => 6
)
)
There is no need to recursion in this way :
$pool = array();
foreach ($array as $value) {
$pool[$value['id']] = $value;
$pool[$value['id']]['children'] = array();
}
foreach ($pool as $k => $v) {
if ($v['id_father']) {
$pool[$v['id_father']]['children'][] = &$pool[$k];
}
else
$parent[] = $v['id'];
}
$result = array();
foreach ($parent as $val) {
$result = $result + $pool[$val];
}
print_r($result);
Related
I have a single-dimensional array of categories; some of which are children of other categories, and some of which contain 'elements'. I need to turn it into a multidimensional array, and remove any categories which have no elements in them or any of their children (or children's children...).
I have the following array:
$category_array = array(
1 => array(
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
),
4 => array(
'elementcount' => 0,
'parentcat' => 1,
'depth' => 2
),
8 => array(
'elementcount' => 0,
'parentcat' => 4,
'depth' => 3
),
9 => array(
'elementcount' => 2,
'parentcat' => 4,
'depth' => 3
),
11 => array(
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
),
12 => array(
'elementcount' => 0,
'parentcat' => 11,
'depth' => 2
),
21 => array(
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
)
);
and I need the following array:
$multidimensional_array = array(
1 => array(
'elementcount' => 3,
'children' => array(
4 => array(
'elementcount' => 0,
'children' => array(
9 => array(
'elementcount' => 2
)
)
)
)
),
11 => array(
'elementcount' => 3,
),
21 => array(
'elementcount' => 3,
)
);
How can this be achieved?
This would be an approach:
<?php
$input = [
1 => [
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
],
4 => [
'elementcount' => 0,
'parentcat' => 1,
'depth' => 2
],
8 => [
'elementcount' => 0,
'parentcat' => 4,
'depth' => 3
],
9 => [
'elementcount' => 2,
'parentcat' => 4,
'depth' => 3
],
11 => [
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
],
12 => [
'elementcount' => 0,
'parentcat' => 11,
'depth' => 2
],
21 => [
'elementcount' => 3,
'parentcat' => 0,
'depth' => 1
]
];
$maxDepth = max(array_column($input, 'depth'));
// handle elements from higher to lower depth
for ($d = $maxDepth; $d >= 0; $d--) {
array_walk($input, function(&$entry, $index) use (&$input, $d) {
if (isset($entry['depth']) && $entry['depth'] == $d) {
// omit entries without elements or elements in children
if ($entry['elementcount'] < 1 && empty($entry['children'])) {
unset($input[$index]);
// handle as child entry of a parent entry
} else if (array_key_exists($entry['parentcat'], $input)) {
$input[$entry['parentcat']]['children'][$index] = [
'elementcount' => $entry['elementcount'],
'children' => isset($entry['children']) ? $entry['children'] : []
];
unset($input[$index]);
// handle as ordinary entry
} else {
$input[$index] = [
'elementcount' => $entry['elementcount'],
'children' => isset($entry['children']) ? $entry['children'] : []
];
}
}
});
}
print_r($input);
The strategy:
handle higher depths first so that the order of input elements does not matter
for each element check if the parent exists, if so stuff it in there
redefine all handled elements
The obvious output is:
(
[1] => Array
(
[elementcount] => 3
[children] => Array
(
[4] => Array
(
[elementcount] => 0
[children] => Array
(
[9] => Array
(
[elementcount] => 2
[children] => Array
(
)
)
)
)
)
)
[11] => Array
(
[elementcount] => 3
[children] => Array
(
)
)
[21] => Array
(
[elementcount] => 3
[children] => Array
(
)
)
)
I took the liberty to create a slightly modified result compared to your suggestion:
The 'children' property always exists as an array. That makes the usage of the result easier and more robust later on. I'd say that in general all elements inside a structure should have identical structure themselves if possible ...
function isParent($id, $list) : bool {
foreach($list as $item) {
if ($item['parentcat'] === $id) {
return true;
}
}
return false;
}
function buildLevel($parent, $catsByParent) : array {
$result = $catsByParent[$parent] ?? [];
foreach($result as $id => $cat) {
if (isset($catsByParent[$id])) {
$result[$id]['children'] = buildLevel($id, $catsByParent);
unset($catsByParent[$id]);
}
}
return $result;
}
// Filter out empty categories
$cats = array_filter(
$category_array,
function($cat, $id) use($category_array) {
return $cat['elementcount']>0 || isParent($id, $category_array);
},
ARRAY_FILTER_USE_BOTH
);
$catsByParent = [];
// Build cats list keyed by parentcat
foreach($cats as $id => $cat) {
$parent = $cat['parentcat'];
unset($cat['parentcat'], $cat['depth']);
$catsByParent[$parent] = ($catsByParent[$parent] ?? []) + [$id => $cat];
}
// Build result
$multidimensional_array = buildLevel(0, $catsByParent);
print_r($multidimensional_array);
First one filters out empty elements, i.e "categories which have no elements in them or any of their children". (children's children requirement sounds strange, wouldn't that be "any of their children" one level further down?)
Then the remaining categories are grouped/sorted by parentcat, aka "level id", to make the list workable :).
Then that list is traversed, starting with level id 0 at the top, and recursively processed(the children) as deep down as needed.
I found sample PHP code for creating JSON based on id<->parent and it looks like:
<?php
$rows = array(
array('id' => 1, 'parent' => 0, 'name' => 'John Doe'),
array('id' => 2, 'parent' => 1, 'name' => 'Sally Smith'),
array('id' => 3, 'parent' => 2, 'name' => 'Mike Jones'),
array('id' => 4, 'parent' => 3, 'name' => 'Jason Williams'),
array('id' => 5, 'parent' => 4, 'name' => 'Sara Johnson'),
array('id' => 6, 'parent' => 1, 'name' => 'Dave Wilson'),
array('id' => 7, 'parent' => 2, 'name' => 'Amy Martin'),
);
// create an index on id
$index = array();
foreach($rows as $row){
$row['data'] = (object) [];
$index[$row['id']] = $row;
}
// build the tree
foreach($index as $id => &$row){
if ($id === 0) continue;
$parent = $row['parent'];
$index[$parent]['children'][] = &$row;
}
unset($row);
// obtain root node
$index = $index[0]['children'][0];
// output json
header('Content-Type: application/json');
echo json_encode($index, JSON_PRETTY_PRINT);
it works fine but how to display all children records for specific ID? For example, I need all members for parent 2 so I try:
$index = $index[2]['children'][0];
and it display only 3 records and it should display 4 of them (Amy Martin is missing).
Thank you.
It is because you are trying to get 0 index so only 1st hierarchy will be displayed. To access all children remove [0] from your data
It should be:
$index = $index[2]['children'];
You only get 3 records because your 'children' are nested on different levels (the 4th one is on a different level
Array
(
[0] => Array
(
[id] => 3
[parent] => 2
[name] => Mike Jones
...
[children] => Array
(
[0] => Array
(
[id] => 4
[parent] => 3
[name] => Jason Williams
[children] => Array
(
[0] => Array
(
[id] => 5
[parent] => 4
[name] => Sara Johnson
...
)
)
)
)
)
[1] => Array
(
[id] => 7
[parent] => 2
[name] => Amy Martin
...
)
)
In order to get all the children you need to traverse and flatten your array. This can be done using an interator.
Full code sample:
<?php
$rows = array(
array('id' => 1, 'parent' => 0, 'name' => 'John Doe'),
array('id' => 2, 'parent' => 1, 'name' => 'Sally Smith'),
array('id' => 3, 'parent' => 2, 'name' => 'Mike Jones'),
array('id' => 4, 'parent' => 3, 'name' => 'Jason Williams'),
array('id' => 5, 'parent' => 4, 'name' => 'Sara Johnson'),
array('id' => 6, 'parent' => 1, 'name' => 'Dave Wilson'),
array('id' => 7, 'parent' => 2, 'name' => 'Amy Martin'),
);
// create an index on id
$index = array();
foreach($rows as $row){
$row['data'] = (object) [];
$index[$row['id']] = $row;
}
// build the tree
foreach($index as $id => &$row){
if ($id === 0) continue;
$parent = $row['parent'];
$index[$parent]['children'][] = &$row;
}
unset($row);
$array = $index[2]['children'];
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$data = array();
foreach ($iterator as $leafValue) {
if ( !$iterator -> hasChildren() ) {
if ($iterator->key() == 'id') {
$data[$leafValue]['id'] = $leafValue;
$id = $leafValue;
} else{
$data[$id][$iterator->key()] = $leafValue;
}
}
}
// output json
header('Content-Type: application/json');
echo json_encode($data, JSON_PRETTY_PRINT);
Output
{
"3": {
"id": 3,
"parent": 2,
"name": "Mike Jones"
},
"4": {
"id": 4,
"parent": 3,
"name": "Jason Williams"
},
"5": {
"id": 5,
"parent": 4,
"name": "Sara Johnson"
},
"7": {
"id": 7,
"parent": 2,
"name": "Amy Martin"
}
}
This is my array:
$arr = array(
0 => array(
'title' => 'test1',
'count' => 4,
'month' => 'jan-2015'
),
1 => array(
'title' => 'test2',
'count' => 10,
'month' => 'jan-2015'
),
2 => array(
'title' => 'test3',
'count' => 14,
'month' => 'jun-2015'
),
3 => array(
'title' => 'test4',
'count' => 45,
'month' => 'july-2015'
),
);
I've to convert this array into multi-dimentional array as below:
$arr = array(
'jan-2015' => array(
0 => array(
'title' => 'test1',
'count' => 4,
),
1 => array(
'title' => 'test2',
'count' => 10,
),
),
'jun-2015' => array(
0 => array(
'title' => 'test3',
'count' => 14,
),
),
'july-2015' => array(
0 => array(
'title' => 'test4',
'count' => 45,
),
),
);
I've tried to make it as expected but unfortunately i can't make this.
Is any other solutions for this?
According to your data structure :
$arr = array(
0 => array(
'title' => 'test1',
'count' => 4,
'month' => 'jan-2015'
),
1 => array(
'title' => 'test2',
'count' => 10,
'month' => 'jan-2015'
),
2 => array(
'title' => 'test3',
'count' => 14,
'month' => 'jun-2015'
),
3 => array(
'title' => 'test4',
'count' => 45,
'month' => 'july-2015'
),
);
try this:
$newArray = array();
foreach($arr as $key => $val) {
$newArray[$val['month']][] = $val;
}
echo '<pre>'.print_r($newArray,1).'</pre>';
Output:
Array
(
[jan-2015] => Array
(
[0] => Array
(
[title] => test1
[count] => 4
[month] => jan-2015
)
[1] => Array
(
[title] => test2
[count] => 10
[month] => jan-2015
)
)
[jun-2015] => Array
(
[0] => Array
(
[title] => test3
[count] => 14
[month] => jun-2015
)
)
[july-2015] => Array
(
[0] => Array
(
[title] => test4
[count] => 45
[month] => july-2015
)
)
)
You could use this function:
function transform($input) {
// Extract months, and use them as keys, with value set to empty array
// The array_fill_keys also removes duilicates
$output = array_fill_keys(array_column($input, 'month'), array());
foreach ($input as $element) {
$copy = $element;
// remove the month key
unset($copy["month"]);
// assign this to the month key in the output
$output[$element["month"]][] = $copy;
}
return $output;
}
Call it like this:
$arr = array(
0 => array(
'title' => 'test1',
'count' => 4,
'month' => 'jan-2015'
),
1 => array(
'title' => 'test2',
'count' => 10,
'month' => 'jan-2015'
),
2 => array(
'title' => 'test3',
'count' => 14,
'month' => 'jun-2015'
),
3 => array(
'title' => 'test4',
'count' => 45,
'month' => 'july-2015'
),
);
print_r (transform($arr));
Output:
Array
(
[jan-2015] => Array
(
[0] => Array
(
[title] => test1
[count] => 4
)
[1] => Array
(
[title] => test2
[count] => 10
)
)
[jun-2015] => Array
(
[0] => Array
(
[title] => test3
[count] => 14
)
)
[july-2015] => Array
(
[0] => Array
(
[title] => test4
[count] => 45
)
)
)
By using answer of #Girish Patidar, You can achieve this by:
$outputArr = array();
$to_skip = array();
foreach($arr as $row){
$to_skip = $row;
unset($to_skip['month']);
$outputArr[$row['month']][] = $to_skip;
}
echo "<pre>";
print_r($outputArr);
die;
There could many way to do this. Please try this one if it works for you
<?php
$newArr=NULL;
foreach($arr as $array)
{
$temp=NULL;
$temp['title']=$array['title'];
$temp['count']=$array['count'];
$newArr[$array['month']][]=$temp;
}
var_dump($newArr);
?>
Ii everyone, I have an post data in array like this, I'm so confused how create the logic in controller:
POST Data:
Array
(
[nama_agenda] => blalala
[kilasan] => asdsadsadasd
[tgl_agenda] => 2014-06-01
[jam_agenda] => 13:27:30
[komisi] => Array
(
[0] => 1
[1] => 3
)
[fraksi] => Array
(
[0] => 1
[1] => 4
)
[badan] => Array
(
[0] => 1
[1] => 3
)
[anggota] => Array
(
[0] => 1
[1] => 4
)
[bagian] => Array
(
[0] => 2
[1] => 4
)
)
My question is how to insert into database, in controller? Thank's for help. I'll appreciate.
Since your structure is not well formed for insert_batch method. Your need to restructure it first. Consider this example:
$original_values = array(
'nama_agenda' => 'blalala',
'kilasan' => 'asdsadsadasd',
'tgl_agenda' => '2014-06-01',
'jam_agenda' => '13:27:30',
'komisi' => array(1, 3),
'fraksi' => array(1, 4),
'badan' => array(1, 3),
'anggota' => array(1, 4),
'bagian' => array(2, 4),
);
$new_values = array();
for($x = 0, $size = count($original_values['komisi']); $x < $size; $x++) {
foreach($original_values as $key => &$value) {
if(!is_array($value)) {
$new_values[$x][$key] = $value;
} else {
$new_values[$x][$key] = array_shift($value);
}
}
}
echo '<pre>';
print_r($new_values);
Should yield something like:
Array
(
[0] => Array
(
[nama_agenda] => blalala
[kilasan] => asdsadsadasd
[tgl_agenda] => 2014-06-01
[jam_agenda] => 13:27:30
[komisi] => 1
[fraksi] => 1
[badan] => 1
[anggota] => 1
[bagian] => 2
)
[1] => Array
(
[nama_agenda] => blalala
[kilasan] => asdsadsadasd
[tgl_agenda] => 2014-06-01
[jam_agenda] => 13:27:30
[komisi] => 3
[fraksi] => 4
[badan] => 3
[anggota] => 4
[bagian] => 4
)
)
Now you can use insert_batch() method.
$this->db->insert_batch('table_name', $new_values);
get all the data in array using $this->input->post() eg:
$bagian= $this->input->post('bagian');
and create a array()
$arr=array(
'db_table_col_1'=>$bagian,
'db_table_col_2'=>$post_data,
'db_table_col_2'=>$post_data
);
pass this array to model
$this->your_model_name->function_name($arr);
then in model create function
function_name($arg){
$this->db->insert('table_name',$arr);
}
if you want to insert multiple row then just use foreach
<?php
$arr1=array();
$arr= array(
'nama_agenda' => 'blalala',
'kilasan' => 'asdsadsadasd',
'tgl_agenda' => '2014-06-01',
'jam_agenda' => '13:27:30',
'komisi' => array
(
'0' => 1,
'1' => 3
),
'fraksi' => array
(
'0' => 1,
'1' => 4
),
'badan' => array
(
'0' => 1,
'1' => 3
),
'anggota' => array
(
'0' => 1,
'1' => 4
),
'bagian' => array
(
'0' => 2,
'1' => 4
)
);
foreach($arr as $row){
if(is_array($row)){
array_push($arr1,$row);
}
}
print_r($arr1);
and then pass this array to batch_insert
function_name($arr1){
$this->db->insert_batch('table_name',$arr1);
}
note arr1 syntax must be
$arr1 = array(
array(
'table_col1' => 'My title' ,
'table_col2' => 'My Name'
),
array(
'table_col1' => 'other title' ,
'table_col2' => 'other Name'
)
);
?>
I have an array like this, how can I split it into several other arrays, based on the category ID?
i.e. have category 1 -> forum 1, forum 4. category 2 -> forum 3, forum 7
I've looked at array_chunk, but there could be any number of categories or forums
Array
(
[category_id] => 1
[category_name] => Main
[forum_id] => 1
[forum_name] => General forum
)
Array
(
[category_id] => 1
[category_name] => Main
[forum_id] => 2
[forum_name] => Test forum
)
Array
(
[category_id] => 2
[category_name] => Test
[forum_id] => 3
[forum_name] => Another test
)
I'm using this SQL query (incase it can be done with SQL only before modifying it with PHP)
$forums_query = mysqli_query($sql_connect, 'SELECT `categories`.`category_id`, `categories`.`category_name`,
`forums`.`forum_id`, `forums`.`forum_name`, `forums`.`category_id`
FROM `categories`
LEFT JOIN `forums`
ON `categories`.`category_id` = `forums`.`category_id`
ORDER BY `categories`.`category_position`, `forums`.`forum_position` ASC');
Edit: by the way, I can think of one way to do it, which is loop through it, and for every cat ID (as X), check if an array exists called catX and if not create it
However, this will run every time someone visits the page, so I want it to be as quick and light on resources as possible
I'm not sure if I get this right, take a look at this demo:
<?php
$forums[] = array('category_id' => 1, 'category_name' => 'Main', 'forum_id' => 1, 'forum_name' => 'General forum');
$forums[] = array('category_id' => 1, 'category_name' => 'Main', 'forum_id' => 2, 'forum_name' => 'General 2');
$forums[] = array('category_id' => 1, 'category_name' => 'Main', 'forum_id' => 3, 'forum_name' => 'General 3');
$forums[] = array('category_id' => 1, 'category_name' => 'Main', 'forum_id' => 4, 'forum_name' => 'General 4');
$forums[] = array('category_id' => 2, 'category_name' => 'Main2', 'forum_id' => 1, 'forum_name' => 'General forum');
$forums[] = array('category_id' => 2, 'category_name' => 'Main2', 'forum_id' => 2, 'forum_name' => 'General 2');
$forums[] = array('category_id' => 3, 'category_name' => 'Main3', 'forum_id' => 3, 'forum_name' => 'General 3');
$forums[] = array('category_id' => 4, 'category_name' => 'Main3', 'forum_id' => 4, 'forum_name' => 'General 4');
$forums_organized = array();
foreach($forums as $forum){
$forums_organized[$forum['category_id']][] = array('forum_name' => $forum['forum_name'], 'forum_id' => $forum['forum_id']);
}
print_r($forums_organized);
output:
Array
(
[1] => Array
(
[0] => Array
(
[forum_name] => General forum
[forum_id] => 1
)
[1] => Array
(
[forum_name] => General 2
[forum_id] => 2
)
[2] => Array
(
[forum_name] => General 3
[forum_id] => 3
)
[3] => Array
(
[forum_name] => General 4
[forum_id] => 4
)
)
[2] => Array
(
[0] => Array
(
[forum_name] => General forum
[forum_id] => 1
)
[1] => Array
(
[forum_name] => General 2
[forum_id] => 2
)
)
[3] => Array
(
[0] => Array
(
[forum_name] => General 3
[forum_id] => 3
)
)
[4] => Array
(
[0] => Array
(
[forum_name] => General 4
[forum_id] => 4
)
)
)
isn't it INNER JOIN in your case ?!
$old_array = array();
$new_array = array();
foreach($old_array AS $tab) {
if(!array_key_exists($tab['category_name'], $new_array)) {
$tmp = array($tab['forum_name']);
$tmp = array($tab['category_name']=>$tmp);
$new_array = array_merge($new_array, $tmp);
}
else {
array_push($new_array[$tab['category_name']], $tab['forum_name']);
}
}