Madalin Alexandru
Madalin Alexandru

Reputation: 85

Tree map by categories

I'm trying to build a tree-map from categories.

I have the categories (I have a lot of categories and I want to remove duplicates and show them in a tree-map view)

$cat = array(
    "Sneakers/Men",
    "Sneakers/Women",
    "Accessories/Jewellery/Men",
    "Accessories/Jewellery/Women",
    "Accessories/Jewellery/Men
");

...and I want them like this

$categories = array(
    "Sneakers" => array(
        "Men" => array(),
        "Women" => array()
    ),
    "Accessories" => array(
        "Jewellery" => array(
            "Men" => array(),
            "Women" => array()
        )
    )
);

to print them like this

- Sneakers
-- Men
-- Women

- Accessories
-- Jewellery
--- Men
--- Women

Upvotes: 0

Views: 364

Answers (2)

cFreed
cFreed

Reputation: 4482

The most simple way is to use references, like this:

$out = [];
foreach ($cat as $str) {
    $lookup =& $out;
    foreach (explode("/", $str) as $part) {
        $lookup =& $lookup[$part];
        if (!isset($lookup)) {
            $lookup = [];
        }
    }
}

$lookup initially refers to the whole expected result, then the reference is extended at each step to follow the path of nested members.

Note that each new member added looks like member-name => [], so that actually even final leaves are arrays: it may seem a bit weird, but is a pretty way to have a reduced code (each member is always ready to receive children).

And it's not a difficulty, though, to use the resulting array to then print it like the OP asked:

function nest_print($src, $level = 0) {
    $prefix = '<br />' . str_repeat('- ', ++$level);
    foreach ($src as $key => $val) {
      echo $prefix . $key;
      if ($val) {
          nest_print($val, $level);
      }
    }
}

nest_print($out);

EDIT

Here is an alternate solution, including the count of final leaves, as asked by the OP in his comment:

$out = [];
foreach ($cat as $str) {
    $lookup =& $out;
    $parts = explode("/", $str);
    foreach ($parts as $part) {
        $lookup =& $lookup[$part];
        if (!isset($lookup)) {
            $lookup = [];
        }
        // when $part is a final leaf, count its occurrences
        if ($part == end($parts)) {
            $lookup = is_array($lookup) ? 1 : ++$lookup;
        }
    }
}

(might likely be improved in a more elegant way, though)

And here is how to modify the print-result snippet accordingly:

function nest_print($src, $level = 0) {
    $prefix = '<br />' . str_repeat('- ', ++$level);
    foreach ($src as $key => $val) {
      echo $prefix . $key;
      if (is_array($val)) {
          nest_print($val, $level);
      } else {
          echo ': ' . $val;
      }
    }
}

nest_print($out);

Upvotes: 2

Tomasz Winter
Tomasz Winter

Reputation: 287

Try this:

<?php
$cat = array(
    "Sneakers/Men",
    "Sneakers/Women",
    "Accessories/Jewellery/Men",
    "Accessories/Jewellery/Women",
    "Accessories/Jewellery/Men
");

function buildTree($categories, $result = []){
    $temp = [];
    foreach($categories as $categoryString){
        $catParts = explode('/',$categoryString);
        if(count($catParts) > 1){
            $temp[$catParts[0]][] = str_replace($catParts[0].'/','',$categoryString);
        } else {
            $temp[$catParts[0]] = [];
        }

    }
    foreach($temp as $elemName => $elemVal){
        $result[$elemName] = buildTree($elemVal);
    }
    return $result;
}

var_dump(buildTree($cat));

Upvotes: 2

Related Questions