tags:

views:

486

answers:

5

Hello, I have this array:

Array
(
    [1] => animal
    [1-1] => turtle
    [1-1-1] => sea turtle
    [1-1-2] => box turtle
    [1-1-3] => green turtle
    [1-1-3-1] => green turtle with brown tail
)

and I want some how to convert it into:

Array
(
    [1-title] => animal
    [1-sons] => array(
         [1-1-title] => turtle
         [1-1-sons] => array(
           [1-1-1] => sea turtle
               [1-1-2] => box turtle
           [1-1-3-title] => green turtle
           [1-1-3-sons] => array(
               [1-1-3-title] => green turtle
                   )
        )
           )
)

or maybe you can suggest a better way for organizing the outputted array..

but how to do that?

I know that's not easy task at all, I'm writing a parser that will walk on data and make tree out of them..

Thanks in advance for your help and advices..

+1  A: 

That really depends on how/for what you are going to use the resulting tree. Can you perhaps write down more details about that?

Anti Veeranna
+7  A: 

The easiest way of organizing your data would be in such a way:

array (
  'Animal' =>
  array (
    'Turtle' =>
    array (
      'Sea Turtle',
      'Box Turtle',
      'Green Turtle' =>
      array (
        'Green Turtle With Brown Tail',
      ),
      'Common Turtle',
    ),
  ),
);

// Or, otherwise written (equivalent to the above)

$animals = array();
$animals['Animal'] = array();
$animals['Animal']['Turtle'] = array();
$animals['Animal']['Turtle'][] = 'Sea Turtle';
$animals['Animal']['Turtle'][] = 'Box Turtle';
$animals['Animal']['Turtle']['Green Turtle'] = array();
$animals['Animal']['Turtle']['Green Turtle'][] = 'Green Turtle With Brown Tail';
$animals['Animal']['Turtle'][] = 'Common Turtle';

Essentially, the name of the animal is the value, unless it has children, then the value is an array and the key is the animal name.


That way, you can easily parse the values by doing the following:

parse_animals($animals);

function parse_animals($array, $indent = 0) {
  if(!is_array($array)) return;    // A little safe guard in case.

  foreach($array as $key => $value) {
    echo str_repeat('  ', $indent) . "- ";

    if(is_array($value)) {
      echo $key . "\n";
      parse_animals($value, $indent + 1);
    } else {
      echo $value . "\n";
    }
  }
}

The above in the console will output the following:

- Animal
  - Turtle
    - Sea Turtle
    - Box Turtle
    - Green Turtle
      - Green Turtle With Brown Tail
    - Common Turtle

EDIT: And here is a version that will output it for a webpage.

function parse_animals_web($array) {
  if(!is_array($array)) return;    // A little safe guard in case.

  foreach($array as $key => $value) {
    echo '<ul>';

    if(is_array($value)) {
      echo '<li>' . htmlentities($key) . "</li>";
      parse_animals_web($value);
    } else {
      echo '<li>' . htmlentities($value) . "</li>";
    }

    echo '</ul>';
  }
}

The output is:

  • Animal
    • Turtle
    • Sea Turtle
    • Box Turtle
    • Green Turtle
      • Green Turtle With Brown Tail
    • Common Turtle


Maybe you want to get the children of an animal.

function get_children_of($array, $name) {
  foreach($array as $key => $value) {
    if(is_array($value)) {
      if($key === $name) {
        return $value;
      } else {
        return get_children_of($value, $name);
      }
    }
  }

  return array();
}

Now we can get all the children of the Green Turtle and output them.

$green_turtle = get_children_of($animals, 'Green Turtle');
parse_array($green_turtle);

The output is:

- Green Turtle With Brown Tail


EDIT: Since you say you are stuck with the input array being in that weird format, here is a function that will convert your array into the format I specified above:

function convert_array($array) {
  $new_array = array();

  $keys = array_keys($array);
  foreach($keys as $key) {
    $level = explode('-', $key);
    $cur_level = &$new_array;
    $cur_key = '';

    foreach($level as $o_key) {
      $cur_key = ltrim($cur_key . '-' . $o_key, '-');
      $next_key = $cur_key . '-1';
      $value = $array[$cur_key];
      $has_child = array_key_exists($next_key, $array);

      if($has_child) {
        if(!array_key_exists($value, $cur_level)) {
          $cur_level[$value] = array();
        }
        $cur_level = &$cur_level[$value];
      } else {
        $cur_level[] = $value;
      }
    }
  }

  return $new_array;
}
Andrew Moore
yes that will be the best way to organize my data, I found that get_children_of function is very useful. but the parse_animals is just not displaying the data the way you said, or it's not working for me. I can only send an array that looks like :Array( [1] => animal [1-1] => turtle [1-1-1] => sea turtle [1-1-2] => box turtle [1-1-3] => green turtle [1-1-3-1] => green turtle with brown tail)then I'm free in the way it's outputted, but the input can't be changed...
Bassel Safadi
I added a function to convert the array into the format I specified above.
Andrew Moore
As for parse_animals, I designed it for console output (not webpage, but walking down the array is the same for both). If you use parse_animals on a webpage, you'll just get a single line. I'll add webpage output.
Andrew Moore
Amazing, that function parse_animals_web is doing the final output for me, crazy, thank you so much Andrew, and the convert_array is doing much better structure for my " crazy looking" input
Bassel Safadi
+1 for utter admiration at the combination of effectiveness and service
Smandoli
A: 

Try this:

$array = array(
    '1' => 'animal',
    '1-1' => 'turtle',
    '1-1-1' => 'sea turtle',
    '1-1-2' => 'box turtle',
    '1-1-3' => 'green turtle',
    '1-1-3-1' => 'green turtle with brown tail'
);
$tree = array();
foreach ($array as $path => $val) {
    $segments = explode('-', $path);
    $last = array_pop($segments);
    $tmp = &$tree;
    $path = '';
    foreach ($segments as $segment) {
        $path .= $segment.'-';
        if (!isset($tmp[$path.'sons'])) {
            $tmp[$path.'sons'] = array();
        }
        $tmp = &$tmp[$path.'sons'];
    }
    $tmp[$path.$last.'-title'] = $val;
}
print_r($tree);

But your data structure doesn’t make much sense.

Gumbo
cool, that's exactly what I want.. Thank you so much Gumbo
Bassel Safadi
I recommend you like the others here to use a different, more versatile data structure than this prefix thing.
Gumbo
while the 1-1-2 (the array keys) are not actually generated by humans but it's just a part of array created by a text parser so I think there will be no place for errors or conflicts if that's why you recommend not using this prefix thing..
Bassel Safadi
What’s wrong? Why did I get down-voted?
Gumbo
nothing wrong at all, I don't know, I try to vote but it say that I must have at least 15 points. I can't vote..
Bassel Safadi
Your answer works for me.. I'm really sorry that you get down voted.and the system is missing the feature of accepting multi answers. that's stupid
Bassel Safadi
A: 
$result = array();
foreach ($array as $position => $text) {
    $p = explode('-', $position); 
    putIntoTree($result, $p, $text);
}

function putIntoTree( &$tree, $posInfo, $item ) {
    $index = array_shift($posInfo) - 1;

    if (!count($posInfo)) {
        $tree[$index]['name'] = $item;
    } else {         
        if (!isset($tree[$index]['children'])) {
            $tree[$index]['children'] = array();
        }
        putIntoTree($tree[$index]['children'], $posInfo, $item);
    }   
}

Results in this, which seems a reasonable way to hold the data.

Array
(
    [0] => Array
        (
            [name] => animal
            [children] => Array
                (
                    [0] => Array
                        (
                            [name] => turtle
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [name] => sea turtle
                                        )

                                    [1] => Array
                                        (
                                            [name] => box turtle
                                        )

                                    [2] => Array
                                        (
                                            [name] => green turtle
                                            [children] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [name] => green turtle with brown tail
                                                        )
                                                )
                                        )
                                )
                        )
                )
        )
)
Tom Haigh
Thanks Tom, holding the data in such output is more clear, I should manage a way to combine the outputs and results and functions I got here to improve the class
Bassel Safadi
A: 

What you are trying to achieve is some kind of a nested set and therefore the easiest way to implement it would be to save the parent id in the children's entry:

// the tree
0 => array(parent => NULL, name => turtle),
1 => array(parent => 0, name => green turtle),
2 => array(parent => 0, name => blue turtle),
3 => array(parent => 1, name => green turtle with yellow nose)

You can walk through this hierarchy using a simple recursive function.

If you use objects instead of associative arrays you even get some performance increase.

Till Theis