Asked  7 Months ago    Answers:  5   Viewed   33 times

I've got a one-dimensional array of objects that represent multi-dimensional data:

array(
    array(
        "id" => 45,
        "parent_id" => null
    ),
    array(
        "id" => 200,
        "parent_id" => 45
    ),
    array(
        "id" => 345,
        "parent_id" => 45
    ),
    array(
        "id" => "355",
        "parent_id" => 200
    )
);

How should I convert it into a multi-dimensional array:

array(
    array(
        "id" => 45,
        "parent_id" => null,
        "children" => array(
            array(
                "id" => 200,
                "parent_id" => 45,
                "children" => array(
                    "id" => "355",
                    "parent_id" => 200
                )

            ),
            array(
                "id" => 345,
                "parent_id" => 45
            ),
        )
    ),
);

 Answers

44

The following code-example converts the array $array into the tree-structure you're looking for:

// key the array by id
$keyed = array();
foreach($array as &$value)
{
    $keyed[$value['id']] = &$value;
}
unset($value);
$array = $keyed;
unset($keyed);

// tree it
$tree = array();
foreach($array as &$value)
{
    if ($parent = $value['parent_id'])
        $array[$parent]['children'][] = &$value;
    else
        $tree[] = &$value;
}
unset($value);
$array = $tree;
unset($tree);

var_dump($array); # your result

This does not work, if there is an existing parent id that is 0. But could be easily changed to reflect that.

This is a related question, that has the original array already keyed, so the first half of the solution could be spared: Nested array. Third level is disappearing.

Edit:

So how does this work? This is making use of PHP variable aliasing (also known as references) and (temporary) arrays that are used to store a) aliases to the nodes ($keyed) and b) to build the new tree order ($tree).

Could you [...] explain the purpose of $array = $keyed, $array = $tree and the unsets?

As both, $keyed and $tree contain references to values in $array, I first copy over that information into $array, e.g.:

$array = $keyed;

As now $keyed is still set (and contains references to the same values as in $array), $keyed is unset:

unset($keyed);

This un-sets all references in $keyed and ensures, that all values in $array aren't referenced any longer (the value's refcount is reduced by one).

If the temporary arrays are not unset after the iteration, their references would still exist. If you use var_dump on $array, you would see that all values would have a & in front, because they are still referenced. unset($keyed) removes these references, var_dump($array) again, and you will see the &s are gone.

I hope this was understandable, references can be hard to follow sometimes if you're not fluent with them. It often helps me to think about them as variable aliases.

If you want some exercise, consider the following:

How to convert your $array from flat to tree with one foreach iteration?

Decide on your own when you would like to click the link which contains a Solution.

Wednesday, March 31, 2021
 
freeMagee
answered 7 Months ago
37

this is called "cartesian product", php man page on arrays http://php.net/manual/en/ref.array.php shows some implementations (in comments).

and here's yet another one:

function array_cartesian() {
    $_ = func_get_args();
    if(count($_) == 0)
        return array(array());
    $a = array_shift($_);
    $c = call_user_func_array(__FUNCTION__, $_);
    $r = array();
    foreach($a as $v)
        foreach($c as $p)
            $r[] = array_merge(array($v), $p);
    return $r;
}

$cross = array_cartesian(
    array('apples', 'pears',  'oranges'),
    array('steve', 'bob')
);

print_r($cross);
Wednesday, March 31, 2021
 
shin
answered 7 Months ago
24

Unfortunately, there's nothing in the Java platform that does this. Btw, you also need to explicitly handle null elements in the Integer[] array (what int are you going to use for those?).

Tuesday, June 8, 2021
 
Sendy
answered 5 Months ago
51

You use a recursive approach but you do not assign the return value of the function call $this->array_values_recursive($value); anywhere. The first level works, as you modify $arr in the loop. Any further level does not work anymore for mentioned reasons.

If you want to keep your function, change it as follows (untested):

function array_values_recursive($arr)
{
  foreach ($arr as $key => $value)
  {
    if (is_array($value))
    {
      $arr[$key] = $this->array_values_recursive($value);
    }
  }

  if (isset($arr['children']))
  {
    $arr['children'] = array_values($arr['children']);
  }

  return $arr;
}
Friday, July 30, 2021
 
kamikaze_pilot
answered 3 Months ago
58

I think the key to the solution is Hash[], which will create a Hash based on an array of key/values, i.e.

Hash[[["key1", "value1"], ["key2", "value2"]]]
#=> {"key1" => "value1", "key2" => "value2"}

Just add a set of map, and you have a solution!

result = [
  {"id_t"=>["1"], "transcript_t"=>["I am a transcript ONE"]},
  {"id_t"=>["2"], "transcript_t"=>["I am a transcript TWO"]},
  {"id_t"=>["3"], "transcript_t"=>["I am a transcript THREE"]}
]
Hash[result.map(&:values).map(&:flatten)]
Wednesday, August 11, 2021
 
Amber
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 :