Asked  7 Months ago    Answers:  5   Viewed   31 times

I have an array with tree data (by parent id). I want to convert it to multidimensional array. What is the best way to achieve that? Is there any short function for that?

Source array:

$source = array(
    '0' => array(
            'Menu' => array(
                    'id' => 45
                    'name' => 'Home'
                    'parent_id' => 1
            )
    )
    '1' => array(
            'Menu' => array(
                    'id' => 47
                    'name' => 'Get started'
                    'parent_id' => 1
            )
    )
    '2' => array(
            'Menu' => array(
                    'id' => 72
                    'name' => 'Attributes'
                    'parent_id' => 71
            )
    )
    '3' => array(
            'Menu' => array(
                    'id' => 73
                    'name' => 'Headings'
                    'parent_id' => 71
            )
    )
    '4' => array(
            'Menu' => array(
                    'id' => 75
                    'name' => 'Links'
                    'parent_id' => 71
            )
    )
    '5' => array(
            'Menu' => array(
                    'id' => 59
                    'name' => 'Images'
                    'parent_id' => 75
            )
    )
    '6' => array(
            'Menu' => array(
                    'id' => 65
                    'name' => 'Lists'
                    'parent_id' => 75
            )
    )
);

Some parents are missing from the source array. I would like the items with missing parent to be root. Result array:

$result = array(
    '0' => array(
            'Menu' => array(
                    'id' => 45
                    'name' => 'Home'
                    'parent_id' => 1
            )
            'Children' => array()
    )
    '1' => array(
            'Menu' => array(
                    'id' => 47
                    'name' => 'Get started'
                    'parent_id' => 1
            )
            'Children' => array()
    )
    '2' => array(
            'Menu' => array(
                    'id' => 72
                    'name' => 'Attributes'
                    'parent_id' => 71
            )
            'Children' => array()
    )
    '3' => array(
            'Menu' => array(
                    'id' => 73
                    'name' => 'Headings'
                    'parent_id' => 71
            )
            'Children' => array()
    )
    '4' => array(
            'Menu' => array(
                    'id' => 75
                    'name' => 'Links'
                    'parent_id' => 71
            )
            'Children' => array(
                    '0' => array(
                        'Menu' => array(
                            'id' => 59
                            'name' => 'Images'
                            'parent_id' => 75
                        )
                        'Children' => array()
                    )
                    '1' => array(
                        'Menu' => array(
                            'id' => 65
                            'name' => 'Lists'
                            'parent_id' => 75
                        )
                        'Children' => array()
                   )
            )
     )
);

Update: removed square brackets.

 Answers

56

I don't think there is a built-in function in PHP that does this.

I tried the following code, and it seems to work to prepare the nested array the way you describe:

$nodes = array();
$tree = array();
foreach ($source as &$node) {
  $node["Children"] = array();
  $id = $node["Menu"]["id"];
  $parent_id = $node["Menu"]["parent_id"];
  $nodes[$id] =& $node;
  if (array_key_exists($parent_id, $nodes)) {
    $nodes[$parent_id]["Children"][] =& $node;
  } else {
    $tree[] =& $node;
  }
}

var_dump($tree);

I wrote a similar algorithm in a PHP class I wrote for my presentation Hierarchical Models in SQL and PHP, but I was using objects instead of plain arrays.

Wednesday, March 31, 2021
 
hakimoun
answered 7 Months ago
42
function set_val(array &$arr, $path,$val)
{
   $loc = &$arr;
   foreach(explode('.', $path) as $step)
   {
     $loc = &$loc[$step];
   }
   return $loc = $val;
}
Wednesday, March 31, 2021
 
laurent
answered 7 Months ago
19

You can do that easily with references:

$out = array();
$cur = &$out;
foreach ($array as $value) {
    $cur[$value] = array();
    $cur = &$cur[$value];
}
$cur = null;

Printing $out should give you:

Array
(
    [one] => Array
        (
            [two] => Array
                (
                    [three] => Array
                        (
                            [four] => 
                        )
                )
        )
)
Wednesday, March 31, 2021
 
maniclorn
answered 7 Months ago
39

When you set cell values individually, you have the option of setting the datatype explicitly, but when you use the fromArray() method, you don't have this option.

However, by default, PHP uses a default value binder to identify datatypes from the values passed, and set the cell datatype accordingly. This default behaviour is defined in a class /PHPExcel/Cell/DefaultValueBinder.php.

So you can create your own value binder, as described in the PHPExcel Documentation, that would set every value as a string datatype.

Something like:

class PHPExcel_Cell_MyColumnValueBinder extends PHPExcel_Cell_DefaultValueBinder implements PHPExcel_Cell_IValueBinder
{
    protected $stringColumns = [];

    public function __construct(array $stringColumnList = []) {
        // Accept a list of columns that will always be set as strings
        $this->stringColumns = $stringColumnList;
    }

    public function bindValue(PHPExcel_Cell $cell, $value = null)
    {
        // If the cell is one of our columns to set as a string...
        if (in_array($cell->getColumn(), $this->stringColumns)) {
            // ... then we cast it to a string and explicitly set it as a string
            $cell->setValueExplicit((string) $value, PHPExcel_Cell_DataType::TYPE_STRING);
            return true;
        }
        // Otherwise, use the default behaviour
        return parent::bindValue($cell, $value);
    }
}

// Instantiate our custom binder, with a list of columns, and tell PHPExcel to use it
PHPExcel_Cell::setValueBinder(new PHPExcel_Cell_MyColumnValueBinder(['A', 'B', 'C', 'E', 'F']));

$objPHPExcel = new PHPExcel();
$objPHPExcel->getActiveSheet()->fromArray($dataArray,null,"A2");
Friday, May 28, 2021
 
Wilk
answered 5 Months ago
59

This will do it:

const sampleArray = ["CONTAINER", "BODY", "NEWS", "TITLE"];
const data = [];    // Starting element.
let current = data; // Pointer to the current element in the loop

sampleArray.forEach(key => {     // For every entry, named `key` in `sampleArray`,
    const next = [];             // New array
    current.push({[key]: next}); // Add `{key: []}` to the current array,
    current = next;              // Move the pointer to the array we just added.
});

console.log(data);

{[key]: next} is relatively new syntax. They're computed property names.

This:

const a = 'foo';
const b = {[a]: 'bar'};

Is similar to:

const a = 'foo';
const b = {};
b[a] = 'bar';

You could re-write the forEach as a one-liner:

const sampleArray = ["CONTAINER", "BODY", "NEWS", "TITLE"];
const data = [];    // Starting element.
let current = data; // Pointer to the current element in the loop

sampleArray.forEach(key => current.push({[key]: current = [] }));

console.log(data);

This current.push works a little counter-intuitively:

  1. Construct a new element to push. This assigns a new value to current.
  2. Push the new element to the reference .push was called on.
    • That reference is the value of current before current = [].
Wednesday, August 11, 2021
 
Carrot
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :