uncovery
uncovery

Reputation: 868

Creating a dynamic hierarchical array in PHP

I have this general data structure:

$levels = array('country', 'state', 'city', 'location');

I have data that looks like this:

$locations = array(
  1 => array('country'=>'USA', 'state'=>'New York', 'city'=>'NYC', 'location'=>'Central Park', 'count'=>123),
  2 => array('country'=>'Germany', ... )
);

I want to create hierarchical arrays such as

$hierarchy = array(
  'USA' => array(
    'New York' => array(
      'NYC' => array(
        'Central Park' => 123,
      ),
    ),
  ),
  'Germany' => array(...),
);

Generally I would just create it like this:

$final = array();
foreach ($locations as $L) {
    $final[$L['country']][$L['state']][$L['city']][$L['location']] = $L['count'];
}

However, it turns out that the initial array $levels is dynamic and can change in values and length So I cannot hard-code the levels into that last line, and I do not know how many elements there are. So the $levels array might look like this:

$levels = array('country', 'state');

Or

$levels = array('country', 'state', 'location');

The values will always exist in the data to be processed, but there might be more elements in the processed data than in the levels array. I want the final array to only contain the values that are in the $levels array, no matter what additional values are in the original data.

How can I use the array $levels as a guidance to dynamically create the $final array?

I thought I could just build the string $final[$L['country']][$L['state']][$L['city']][$L['location']] with implode() and then run eval() on it, but is there are a better way?

Upvotes: 0

Views: 617

Answers (5)

Kazakh
Kazakh

Reputation: 104

try this:

<?php
$locations = [
    ['country'=>'USA', 'state'=>'New York', 'city'=>'NYC', 'location'=>'Central Park', 'street'=>'7th Ave',  'count'=>123],
    ['country'=>'USA', 'state'=>'Maryland', 'city'=>'Baltimore', 'location'=>'Harbor', 'count'=>24],
    ['country'=>'USA', 'state'=>'Michigan', 'city'=>'Lansing', 'location'=>'Midtown', 'building'=>'H2B', 'count'=>7],
    ['country'=>'France', 'state'=>'Sud', 'city'=>'Marseille', 'location'=>'Centre Ville', 'count'=>12],
];

$nk = array();
foreach($locations as $l) {
    $jsonstr = json_encode($l);
    preg_match_all('/"[a-z]+?":/',$jsonstr,$e);

    $narr = array();
    foreach($e[0] as $k => $v) {
        if($k == 0 ) {
            $narr[] = '';
        } else {
            $narr[] = ":{";
        }        
    }
    $narr[count($e[0]) -1] = ":" ;
    $narr[] = "";
    $e[0][] = ",";

    $jsonstr = str_replace($e[0],$narr,$jsonstr).str_repeat("}",count($narr)-3);

    $nk [] = $ko =json_decode($jsonstr,TRUE);

}
print_r($nk);

Upvotes: 0

BeetleJuice
BeetleJuice

Reputation: 40896

Cool question. A simple approach:

$output = []; //will hold what you want
foreach($locations as $loc){
    $str_to_eval='$output';
    for($i=0;$i<count($levels);$i++) $str_to_eval .= "[\$loc[\$levels[$i]]]";
    $str_to_eval .= "=\$loc['count'];";
    eval($str_to_eval); //will build the array for this location
}

Live demo

Upvotes: 2

Blue
Blue

Reputation: 22911

Here's my implementation. You can try it out here:

$locations = array(
  1 => array('country'=>'USA', 'state'=>'New York', 'city'=>'NYC', 'location'=>'Central Park', 'count'=>123),
  2 => array('country'=>'Germany', 'state'=>'Blah', 'city'=>'NY', 'location'=>'Testing', 'count'=>54),
);

$hierarchy = array();

$levels = array_reverse(
    array('country', 'state', 'city', 'location')
);

$lastLevel = 'count';


foreach ( $locations as $L )
{
    $array = $L[$lastLevel];

    foreach ( $levels as $level )
    {
        $array = array($L[$level] => $array);
    }

    $hierarchy = array_merge_recursive($hierarchy, $array);
}

print_r($hierarchy);

Upvotes: 2

SIDU
SIDU

Reputation: 2278

If your dataset always in fixed structure, you might just loop it

$data[] = [country=>usa, state=>ny, city=>...]

to
foreach ($data as $row) {
    $result[][$row[country]][$row[state]][$row[city]] = ...
}

In case your data is dynamic and the levels of nested array is also dynamic, then the following is an idea:

/* convert from [a, b, c, d, ...] to [a][b][...] = ... */

function nested_array($rows, $level = 1) {
    $data = array();
    $keys = array_slice(array_keys($rows[0]), 0, $level);
    foreach ($rows as $r) {
        $ref = &$data[$r[$keys[0]]];
        foreach ($keys as $j => $k) {
            if ($j) {
                $ref = &$ref[$r[$k]];
            }
            unset($r[$k]);
        }
        $ref = count($r) > 1 ? $r : reset($r);
    }
    return $data;
}

Upvotes: 0

Harshad Hirapara
Harshad Hirapara

Reputation: 462

Database have three field: here Name conatin contry state and city name

id,name,parentid

Pass the contry result to array to below function:

$data['contry']=$this->db->get('contry')->result_array();
$return['result']=$this->ordered_menu( $data['contry'],0);
echo "<pre>";
     print_r ($return['result']);
     echo "</pre>";


  Create Function as below:

function ordered_menu($array,$parent_id = 0)
    { 
        $temp_array = array();
        foreach($array as $element)
        {
            if($element['parent_id']==$parent_id)
            {
                $element['subs'] = $this->ordered_menu($array,$element['id']);
                $temp_array[] = $element;
            }
        }
        return $temp_array;
    }

Upvotes: -1

Related Questions