0

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?

4
  • So are you saying that some members of the array $levels may not exist? Like for example, a $L['location'] might not exist? Commented Aug 4, 2016 at 4:32
  • Thanks for the comment, let me clarify above. Commented Aug 4, 2016 at 4:34
  • I'm still working on this @uncovery. Commented Aug 4, 2016 at 5:05
  • @FrankerZ Thanks :) Commented Aug 4, 2016 at 5:05

5 Answers 5

2

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);
Sign up to request clarification or add additional context in comments.

13 Comments

Is this the expected result? If not, please clarify, and I can attempt some other solutions =)
@uncovery be careful. This does not work if you have more than one entry from the same country. See here: 3v4l.org/XUKKS .
You are correct @BeetleJuice. I've updated my answer.
@FrankerZ nice. Your solution is really clever. Thanks for sharing.
@BeetleJuice Yours as well. (I've always tried to stray away from eval, as I've been told it causes performance hits, and can be dangerous), so I try to look for solutions that don't involve eval(). Regardless, your solution works, and is a viable alternative. Upvote for you good sir.
|
2

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

1 Comment

Yeah that's what I thought to do first, basically construct the new array as a string and then eval it, thanks!
0

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;
}

Comments

0

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);

Comments

-1

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;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.