I have an array:
$data_array = array(
array('id'=> 1 , 'parent_id' => 1 , 'name'=>'Air Vehicles' ),
array('id'=> 2 , 'parent_id' => 2 , 'name'=>'Land Vehicles' ),
array('id'=> 3 , 'parent_id' => 3 , 'name'=>'Water Vehicles' ),
array('id'=> 4 , 'parent_id' => 2 , 'name'=>'Bikes' ),
array('id'=> 5 , 'parent_id' => 2 , 'name'=>'Cars' ),
array('id'=> 6 , 'parent_id' => 1 , 'name'=>'Aero Planes'),
array('id'=> 7 , 'parent_id' => 1 , 'name'=>'Helicopter'),
array('id'=> 8 , 'parent_id' => 3 , 'name'=>'Ships'),
array('id'=> 9 , 'parent_id' => 1 , 'name'=>'Hoverboard'),
array('id'=> 10 , 'parent_id' => 2 , 'name'=>'Hoverboard'),
array('id'=> 11 , 'parent_id' => 4 , 'name'=>'R1 Kawasaki'),
array('id'=> 12 , 'parent_id' => 4 , 'name'=>'Suzuki Hayabusa'),
);
I want to create a tree array from above array:
Air Vechiles
-Aero Planes
-Helicopter
-HoverBoard
Land Vechiles
-Bikes
--R1 Kawasaki
--Suzuki Hayabusa
-Cars
Water Vehicles
-Ships
Here is your desired solution.
class CategoryTree{
public $padding_array;
public function __construct(){
$this->padding_array = array();
}
public function getTreeArray($data_array){
foreach($data_array as $i=>$data){
if($data['id'] == $data['parent']) $data_array[$i]['parent'] = 0;
}
return $this->getCategoryTreeArray($data_array);
}
public function getCategoryTreeArray($elements, $parent_id = 0){
$branch = array();
foreach ($elements as $element) {
if ($element['parent'] == $parent_id) {
$children = $this->getCategoryTreeArray($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[$element['id']] = $element;
}
}
return $branch;
}
public function getCatgoryPadingArray($tree_array, array &$padding_array, $depth=0){
foreach ($tree_array as $id => $data_array) {
$padding_array[$id] = str_repeat('-', $depth).$data_array['name'];
if(is_array($data_array) && array_key_exists('children', $data_array)){
$this->getCatgoryPadingArray($data_array['children'], $padding_array , $depth+1);
}
}
}}
Using class:
$data_array = array(); // your array data
$tree_object = new CategoryTree();
$category_tree_array = $tree_object->getTreeArray($data_array);
$tree_object->getCatgoryPadingArray($category_tree_array, $tree_object->padding_array);
$category_padding_array = $tree_object->padding_array;
echo('<pre>');
print_r($category_padding_array);
echo('<pre>');
You can do it with recursive calling of custom function or like this:
$data_array = array(
array('id' => 1, 'parent_id' => 1, 'name' => 'Air Vehicles'),
array('id' => 2, 'parent_id' => 2, 'name' => 'Land Vehicles'),
array('id' => 3, 'parent_id' => 3, 'name' => 'Water Vehicles'),
array('id' => 4, 'parent_id' => 2, 'name' => 'Bikes'),
array('id' => 5, 'parent_id' => 2, 'name' => 'Cars'),
array('id' => 6, 'parent_id' => 1, 'name' => 'Aero Planes'),
array('id' => 7, 'parent_id' => 1, 'name' => 'Helicopter'),
array('id' => 8, 'parent_id' => 3, 'name' => 'Ships'),
array('id' => 9, 'parent_id' => 1, 'name' => 'Hoverboard'),
array('id' => 10, 'parent_id' => 2, 'name' => 'Hoverboard'),
array('id' => 11, 'parent_id' => 4, 'name' => 'R1 Kawasaki'),
array('id' => 12, 'parent_id' => 4, 'name' => 'Suzuki Hayabusa'),
);
$tree = array();
foreach ($data_array as $k => &$val) {
if ($val['parent_id'] == $val['id']) {
if (empty($tree[$val['parent_id']])) {
$tree[$val['parent_id']] = array();
}
$tree[$val['parent_id']] = array_merge($tree[$val['parent_id']], $val);
} else {
$tree[$val['parent_id']]['sub'][] = $val;
}
}
print_r($tree);
For creating a tree array use a Recursive function. This is working perfectly for me :-
foreach($data_array as $i=>$data)
{
if($data['id'] == $data['parent_id']) $data_array[$i]['parent_id'] = 0;
}
function buildTree(array $elements, $parentId = 0)
{
$branch = array();
foreach ($elements as $element)
{
if ($element['parent_id'] == $parentId)
{
$children = buildTree($elements, $element['id']);
if ($children) $element['children'] = $children;
$branch[] = $element;
}
}
return $branch;
}
$tree = buildTree($data_array);
echo '<pre>', print_r($tree, TRUE);
Try this
$new = array();
foreach ($data_array as $a){
$new[$data_array['parentid']][] = $a;
}
$tree = createTree($new, array($data_array[0]));
print_r($tree);
function createTree(&$list, $parent){
$tree = array();
foreach ($parent as $k=>$l){
if(isset($list[$l['id']])){
$l['children'] = createTree($list, $list[$l['id']]);
}
$tree[] = $l;
}
return $tree;
}
If you look at this example, you should be able to figure out that all you need to do is replace the array name and values to accommodate your requirements. Clearly your data is not numbers so you need to find out for yourself how you would deal with the vehicle names.
$tmpArray = array(array("one",array(1,2,3)),array("two",array(4,5,6)),array("three",array(7,8,9)));
And use print to check the structure
print_r($tmpArray);
Related
I have a hierarchical database table like below
ID Name Subcategory ParentID
1 ABC 0
2 DEF QFE 0
3 QFE XYZ 2
4 XYZ MNJ 3
From Thant I have got PHP array like below
$array_name = array(
array('ID' => '1', 'Name' => 'ABC', 'Subcategory' => '', 'ParentID' => '0'),
array('ID' => '2', 'Name' => 'DEF', 'Subcategory' => 'QFE', 'ParentID' => '0'),
array('ID' => '3', 'Name' => 'QFE', 'Subcategory' => 'XYZ', 'ParentID' => '2'),
array('ID' => '4', 'Name' => 'XYZ', 'Subcategory' => 'MNJ', 'ParentID' => '3')
);
but I want array like below
$array_name = array(
array('ID' => '1', 'Name' => 'ABC', 'Subcategory' => '', 'ParentID' => '0'),
array('ID' => '2', 'Name' => 'DEF', 'Subcategory' => array('ID' => '3', 'Name' => 'QFE', 'Subcategory' => array('ID' => '4', 'Name' => 'XYZ', 'Subcategory' => 'MNJ', 'ParentID' => '3'), 'ParentID' => '2'), 'ParentID' => '0'),
);
I want a function which checks is that row have some Subcategory or not and if a row has Subcategory then get that subcategory row as an array and make one array with all category and Subcategory
for that, I have tried to make a function which is given below
function find_subcategory($ID,$con){
$table_name ="SELECT * FROM `table_name` WHERE `parent_id` = '$ID'";
$table_name_result = mysqli_query($con,$table_name);
$category_array = array();
if(mysqli_num_rows($table_name_result)) {
while($row = mysqli_fetch_assoc($table_name_result)) {
$Subcategory= $row['Subcategory'];
$ID = $row['ID'];
if ($Subcategory== '') {
$find_subcategory = find_subcategory($ID,$con);
$row['Subcategory'] = $find_subcategory;
$category_array[] = $row;
}else{
$category_array[] = $row;
}
}
}
return json_encode(array('tbl_category'=>$category_array));
}
but this function is not working to get all the subcategories of one category.
can anybody help me with this
Rather than create a recursive routine, which executes the SQL for each level, this instead reads all of the categories in and them assembles them into the hierarchy.
Note that it reads them in reverse order so that when it assembles them, each subcategory is always read before the parent (More details in code comments)...
$table_name ="SELECT * FROM `category` ORDER BY parent_id DESC, id desc";
$table_name_result = mysqli_query($con,$table_name);
$categories = mysqli_fetch_all($table_name_result, MYSQLI_ASSOC);
$output= [];
foreach ( $categories as $category) {
// If there is a parent for this item
if ( !empty ($category['parent_id']) ) {
// Set basic details
$output[$category['parent_id']]['Subcategory'][$category['id']] = $category;
// If there is already some data (subcategories)
if ( isset($output[$category['id']]) ){
// Copy subcategories
$output[$category['parent_id']]['Subcategory'][$category['id']] +=
$output[$category['id']];
// Remove old node
unset ( $output[$category['id']] );
}
}
else {
// Add in category data (allow for existing data to be added
$output[$category['id']] = $category + ($output[$category['id']]??[]);
}
}
I successfully implemented and tested a recursive routine to solve this. However, for performance reasons I had to decouple the access to the database from the recursive call.
First, you fetch your query into an array as you already have and then recursively rearrange the elements so that the keys are nested in the proper order.
Pretty much of the explanations I'd like to put here are put as comments in the code.
$array_name = array(
array('ID' => '1', 'Name' => 'ABC', 'Subcategory' => '', 'ParentID' => '0'),
array('ID' => '2', 'Name' => 'DEF', 'Subcategory' => 'QFE', 'ParentID' => '0'),
array('ID' => '3', 'Name' => 'QFE', 'Subcategory' => 'XYZ', 'ParentID' => '2'),
array('ID' => '4', 'Name' => 'XYZ', 'Subcategory' => 'MNJ', 'ParentID' => '3'),
array('ID' => '5', 'Name' => 'XYY', 'Subcategory' => 'MNJ', 'ParentID' => '1')
);
// recreate nested array
function get_nested_array($arr) {
$new_arr = array(); // new array to collect each top level element with nesting
foreach ($arr as $key => $value) {
// only top level elements would appear here as they would not be nested
if($value['ParentID'] == '0') {
array_push($new_arr, get_nested_item($value, $arr));
}
}
return $new_arr;
}
// recursive function to perform nesting on each element
function get_nested_item($hay, $stack) {
foreach ($stack as $key => $value) {
if ($hay['ID'] == $value['ParentID']) {
$index = get_key($hay, $stack);
// reduce $stack size by removing the HAY from the STACK
// recursion terminates when $stack size is 0
$stack = $index >= 0 ? array_splice($stack, $index) : [];
// update subcategory of the found nesting
$hay['Subcategory'] = get_nested_item($value, $stack);
}
}
return $hay;
}
// get the position of $hay in a $stack using the ID
function get_key($hay, $stack) {
foreach ($stack as $key => $value) {
if($hay['ID'] == $value['ID']) return $key;
}
return -1;
}
// print array so that it understandable
function print_array($arr) {
foreach ($arr as $key => $value) {
print_r($value);
echo "<br>";
}
}
// Test case
// print array before nesting
print_array($array_name);
echo"<br>";
// print array after nesting
$new_array = get_nested_array($array_name);
print_array($new_array);
Can anyone see why this isn't working?
private static function traverseTree($tree,$node)
{
$nodes = array();
$results = array();
$children = $tree->where('parent_id',$node->id)->where('type',UserGroup::DEPARTMENT_TYPE);
foreach($children as $child){
$nodes[] = $child->id;
$children = self::traverseTree($tree,$child);
$results = array_merge ($nodes,$children);
}
return $results;
}
I am stepping through it and I can see that the children are getting found but they are not included in the final results.
The final results just contain the top level.
Detail added:
$tree is a Laravel Collection, if I pipe this to an array I get:
array (
0 =>
array (
'id' => 21,
'name' => 'Top',
'type' => 1,
'parent_id' => 0,
),
1 =>
array (
'id' => 33,
'name' => 'UKDept',
'type' => 2,
'parent_id' => 21,
),
2 =>
array (
'id' => 36,
'name' => 'UKSubDept',
'parent_id' => 33,
),
3 =>
array (
'id' => 37,
'name' => 'USDept',
'type' => 2,
'parent_id' => 21,
),
)
$node:
array (
'id' => 21,
'name' => 'Top',
'type' => 1,
'parent_id' => 0,
),
The first call to populate $children gives:
array (
0 =>
array (
'id' => 33,
'name' => 'UKDept',
'type' => 2,
'parent_id' => 21,
),
1 =>
array (
'id' => 37,
'name' => 'USDept',
'type' => 2,
'parent_id' => 21,
),
)
Try taking the array outside the function.
$nodes = array();
$results = array();
private static function traverseTree($tree,$node)
{
$children = $tree->where('parent_id',$node->id)->where('type',UserGroup::DEPARTMENT_TYPE);
foreach($children as $child){
$nodes[] = $child->id;
$children = self::traverseTree($tree,$child);
$results = array_merge ($nodes,$children);
}
return $results;
}
It will not reinitialize them each time you call the function.Check if it works?
Looks like $child is an array but you are treating it like an object. Try changing:
$nodes[] = $child->id;
to
$nodes[] = $child['id'];
I have this working now, one stupid mistake I made was:
foreach($children as $child){
$nodes[] = $child->id;
$children = self::traverseTree($tree,$child);
$results = array_merge ($nodes,$children);
Using $children as a variable name twice!
This is the complete function:
private static function traverseTree($tree,$node)
{
$initialId = array();
$results = array();
$results[] = $node->id;
$children = $tree->where('parent_id',$node->id);
foreach($children as $node){
//if this is a department, then we need to go deeper
if($node->type == DEPARTMENT) {
$nodesChildren = self::traverseTree($tree, $node);
$results = array_merge ($results,$nodesChildren);
}else{
//just add this node to the results array
$results[] = $node->id;
}
}
return $results;
}
I just want some help in displaying the correct structure of the image data below. Basically what i want is to display it in nested form. From the data below, I would like it to display like this:
Engel (parent which parent_id = 0)
Chest Fridge - Freezers
Small Engel
digital platinum
Upright Fridge
Built-in fridge
Fridge Accessories
Blankets
Carry handles
I hope anyone can help me. Im using php fyi.
Do it recursively
// For example, data fetched from database:
$data = [
['id' => 1, 'sku' => 'name1', 'parent_id' => null],
['id' => 2, 'sku' => 'name12', 'parent_id' => 1],
['id' => 3, 'sku' => 'name12', 'parent_id' => 1],
['id' => 4, 'sku' => 'name2', 'parent_id' => null],
['id' => 5, 'sku' => 'name123', 'parent_id' => 2],
];
// Recursive function
function outTree($data, $parentId = null) {
$str = '';
foreach ($data as $row) {
if ($row['parent_id'] == $parentId) {
$str .= '<li>' . $row['sku'];
$str .= outTree($data, $row['id']);
$str .= '</li>';
}
}
if ($str) {
$str = '<ul>' . $str . '</ul>';
}
return $str;
}
echo outTree($data);
I have an array what contains multiple indexes like
$arr = array(
1 => array('id' => 0,
'container' => 1
),
2 => array('id' => 1,
'container' => 1
),
3 => array('id' => 2,
'container' => 2
),
4 => array('id' => 3,
'container' => 1
)
);
How can I find and loop through all items where container is 1 and after that loop all items where container is 2 and so on..
Besides from using the wrong array assignment :. Just change it to =>.
Then just use a foreach loop:
$containers = array(1, 2);
foreach($containers as $container) {
foreach ($arr as $value) {
if($container == $value['container']) {
echo "Container: $container : ";
echo $value['id'] . '<br/>';
}
}
}
There are many ways to do this. This one re-structures/groups the array by a given key - which is feasible if you iterate over all elements anyway.
<?php
$arr = array(
1 => array('id' => 0,
'container' => 1
),
2 => array('id' => 1,
'container' => 1
),
3 => array('id' => 2,
'container' => 2
),
4 => array('id' => 3,
'container' => 1
)
);
$byContainer = groupBy($arr, 'container');
foreach($byContainer as $container=>$items) {
echo $container, "\r\n";
foreach($items as $e) {
echo ' ', $e['id'], "\r\n";
}
}
function groupBy($arr, $key) {
$result = array();
foreach($arr as $e) {
if ( !isset($result[ $e[$key] ]) ) {
$result[ $e[$key] ] = array($e);
}
else {
$result[ $e[$key] ][] = $e;
}
}
return $result;
}
<?php
$arr = array(1 => array('id'=> 0, 'container'=> 1),
2 => array('id'=> 1, 'container'=> 1),
3 => array('id'=> 2, 'container'=> 2),
4 => array('id'=> 3, 'container'=> 1));
$temp = array();
foreach ($arr as $elem) {
foreach ($elem as $k => $v) {
if ($k == 'container') {
$temp[$k][$v][] = $v;
}
}
}
print_r($temp);
?>
Working Example
I have got a array in this format:
array(
array('id' => 1, 'parent_id' => null, 'name' => 'lorem ipsum'),
array('id' => 2, 'parent_id' => 1, 'name' => 'lorem ipsum1'),
array('id' => 3, 'parent_id' => 1, 'name' => 'lorem ipsum2'),
array('id' => 4, 'parent_id' => 2, 'name' => 'lorem ipsum3'),
array('id' => 5, 'parent_id' => 3, 'name' => 'lorem ipsum4'),
array('id' => 6, 'parent_id' => null, 'name' => 'lorem ipsum5'),
);
I have to convert this array to json object with this style:
var json = {
id: "1",
name: "loreim ipsum",
data: {},
children: [{
id: "2",
name: "lorem ipsum1",
data: {},
children: [{
id: "3",
name: "lorem ipsum2",
data: {},
children: [{
..............
How can i do this? Thanks.
My solution:
$data = array(
array('id' => 1, 'parent_id' => null, 'name' => 'lorem ipsum'),
array('id' => 2, 'parent_id' => 1, 'name' => 'lorem ipsum1'),
array('id' => 3, 'parent_id' => 1, 'name' => 'lorem ipsum2'),
array('id' => 4, 'parent_id' => 2, 'name' => 'lorem ipsum3'),
array('id' => 5, 'parent_id' => 3, 'name' => 'lorem ipsum4'),
array('id' => 6, 'parent_id' => null, 'name' => 'lorem ipsum5'),
);
$itemsByReference = array();
// Build array of item references:
foreach($data as $key => &$item) {
$itemsByReference[$item['id']] = &$item;
// Children array:
$itemsByReference[$item['id']]['children'] = array();
// Empty data class (so that json_encode adds "data: {}" )
$itemsByReference[$item['id']]['data'] = new StdClass();
}
// Set items as children of the relevant parent item.
foreach($data as $key => &$item)
if($item['parent_id'] && isset($itemsByReference[$item['parent_id']]))
$itemsByReference [$item['parent_id']]['children'][] = &$item;
// Remove items that were added to parents elsewhere:
foreach($data as $key => &$item) {
if($item['parent_id'] && isset($itemsByReference[$item['parent_id']]))
unset($data[$key]);
}
// Encode:
$json = json_encode($data);
Here's code to do what you need. It does not need the items to be in parent-children order in the array, but will finish faster if they are.
Please study the comments to understand what the code is doing and why; and if you still have questions, ask them too!
// Assume your array is $data
$root = new stdClass; // this is your root item
$objectMap = array(); // this holds objects indexed by their id
// Since we need to iterate over the array, but there may be no guarantee
// that an item's parent will be always encountered before the item itself,
// we loop as many times as needed, skipping items whose parent we have not
// yet seen. Hopefully we will see it later and be able to process these
// items in the next iteration.
while (!empty($data)) {
// Remember how many items there are when starting the loop
$count = count($data);
// Do the actual work!
foreach ($data as $key => $row) {
$parentId = $row['parent_id'];
if ($parentId === null) {
// We just met the root element
$current = $root;
}
else if (isset($objectMap[$parentId])) {
// We met an element with a parent that we have already seen
$current = new stdClass;
}
else {
// We met an element with an unknown parent; ignore it for now
continue;
}
// Put the current element on the map so that its children will
// be able to find it when we meet them
$objectMap[$row['id']] = $current;
// Add the item to its parent's children array
$objectMap[$parentId]->children[] = $current;
// Set the item's properties
$current->id = $row['id'];
$current->name = $row['name'];
$current->data = new stdClass; // always empty
$current->children = array();
// We successfully processed this, remove it (see why below!)
unset($data[$key]);
}
// OK, we looped over the array once. If the number of items has
// not been reduced at all, it means that the array contains only
// items whose parents do not exist. Instead of looping forever,
// let's just take what we are given and stop here.
if ($count == count($data)) {
break;
}
// If there are still items in $data, we will now iterate again
// in the hope of being able to process them in the next iteration
}
// All set! If $data is not empty now, it means there were items
// with invalid parent_ids to begin with.
$output = json_encode($root);
My take (I know an answer has been accepted, but I worked on this so I'm gonna post id =P)
// Test data
$data = array(
array('id' => 1, 'parent_id' => null, 'name' => 'lorem ipsum'),
array('id' => 2, 'parent_id' => 1, 'name' => 'lorem ipsum1'),
array('id' => 3, 'parent_id' => 1, 'name' => 'lorem ipsum2'),
array('id' => 4, 'parent_id' => 2, 'name' => 'lorem ipsum3'),
array('id' => 5, 'parent_id' => 3, 'name' => 'lorem ipsum4'),
array('id' => 6, 'parent_id' => null, 'name' => 'lorem ipsum5'),
);
// Randomize, because the data may not be in a top-down order
shuffle( $data );
// Parse and inspect the result
$builder = new TreeBuilder( $data );
echo '<pre>', print_r( $builder->getTree() ), '</pre>';
class TreeBuilder
{
protected $leafIndex = array();
protected $tree = array();
protected $stack;
function __construct( $data )
{
$this->stack = $data;
while( count( $this->stack ) )
{
$this->branchify( array_shift( $this->stack ) );
}
}
protected function branchify( &$leaf )
{
// Root-level leaf?
if ( null === $leaf['parent_id'] )
{
$this->addLeaf( $this->tree, $leaf );
}
// Have we found this leaf's parent yet?
else if ( isset( $this->leafIndex[$leaf['parent_id']] ) )
{
$this->addLeaf( $this->leafIndex[$leaf['parent_id']]['children'], $leaf );
} else {
// Nope, put it back on the stack
$this->stack[] = $leaf;
}
}
protected function addLeaf( &$branch, $leaf )
{
// Add the leaf to the branch
$branch[] = array(
'id' => $leaf['id']
, 'name' => $leaf['name']
, 'data' => new stdClass
, 'children' => array()
);
// Store a reference so we can do an O(1) lookup later
$this->leafIndex[$leaf['id']] = &$branch[count($branch)-1];
}
protected function addChild( $branch, $leaf )
{
$this->leafIndex[$leaf['id']] &= $branch['children'][] = $leaf;
}
public function getTree()
{
return $this->tree;
}
}