0

My database spits out an hierarchy array of data like this:

Array(
    [0] => array(
        [level] => 0
        [wo_number] => foo1
        [parent] => NULL
    )
    [1] => array(
        [level] => 1
        [wo_number] => foo2
        [parent] => foo1
    )
    [2] => array(
        [level] => 2
        [wo_number] => bar1
        [parent] => foo2
    )
    [3] => array(
        [level] => 2
        [wo_number] => bar2
        [parent] => foo2
    )
    [4] => array(
        [level] => 2
        [wo_number] => bar3
        [parent] => foo2
    )
    [5] => array(
        [level] => 2
        [wo_number] => bar4
        [parent] => foo2
    )
    [6] => array(
        [level] => 2
        [wo_number] => bar5
        [parent] => foo2
    )
)

Each array node has a parent key that references another node in the array.

And I need to convert the array to a format like this:

Array(
    [0] => array(
        [level] => 0
        [wo_number] => foo1
        [parent] => NULL
        [children] => array(
            [0] => array(
                [level] => 1
                [wo_number] => foo2
                [parent] => foo1
                [children] => array(
                    etc etc etc
                )
            )
        )
    )
)

So that I can then json_encode it, and use it in a ExtJS TreePanel.

Yoseph
  • 608
  • 9
  • 22
  • 1
    possible duplicate of [PHP Create a Multidimensional Array from an array with relational data](http://stackoverflow.com/questions/11239652/php-create-a-multidimensional-array-from-an-array-with-relational-data) – Yoshi Sep 18 '12 at 19:50
  • Why not just store the JSON data directly in a NoSQL database? You would eliminate any need for transformation. – Mike Brant Sep 18 '12 at 19:50
  • your db does NOT return an array, you are formatting the db results that way with php, so change how you do that –  Sep 18 '12 at 19:52
  • Yoshi comment is very similar. Except when I run that makeRecursive function, it only returns the first child on down. But it is very similar to my needs. I just wish the makeRecursive() was human-readable :( – Yoseph Sep 18 '12 at 20:39

3 Answers3

1

Using a slighlty changed function (from what I linked in the comments):

/**
 * Helper function
 * 
 * @param array  $d flat data, implementing a id/parent id (adjacency list) structure
 * @param mixed  $r root id, node to return
 * @param string $p parent id index
 * @param string $k id index
 * @param string $c children index
 * @return array
 */
function flat2nested($d, $r = 0, $p = 'parent', $k = 'id', $c = 'children') {
  $m = array();
  foreach ($d as $e) {
    isset($m[$e[$p]]) ?: $m[$e[$p]] = array();
    isset($m[$e[$k]]) ?: $m[$e[$k]] = array();
    $m[$e[$p]][] = array_merge($e, array($c => &$m[$e[$k]]));
  }

  return $m[$r]; // removed the `[0]` here
}

You can go with an easy call, such as:

flat2nested($input, null, $p = 'parent', $k = 'wo_number');

though currently no leaf index would be set, but as this seems to allways be false it would be easier to set it in the input array.

demo: http://codepad.viper-7.com/IKRLUO

If you have questions about the inner workings of flat2nested (or makeRecursive) just leave a comment and I'll extend my answer.

Yoshi
  • 54,081
  • 14
  • 89
  • 103
  • Thanks for pointing out my unnecessary loop at the end and just returning $m[$r] as the children. I have updated my function above to what I am using now. – Yoseph Sep 19 '12 at 17:31
  • Is there a nested2flat() one as well up your sleeve? – Yoseph Oct 02 '12 at 16:08
  • @Pyrite Well, that would be a standard [tree traversal](http://en.wikipedia.org/wiki/Tree_traversal) problem. For which there are many good methods readily available. – Yoshi Oct 03 '12 at 18:02
  • Yea, I see that now. I ended up making one in Javascript to only get the data I need from the tree. – Yoseph Oct 03 '12 at 20:39
0

I modified the makeRecursive() function that Yoshi pointed me to to do the following:

    public function makeRecursive($d, $r = 0, $pk = 'parent', $k = 'wo_number', $c = 'children') {
            $m = array();
            foreach ($d as $e) {
                    isset($m[$e[$pk]]) ?: $m[$e[$pk]] = array();
                    isset($m[$e[$k ]]) ?: $m[$e[$k ]] = array();
                    $m[$e[$pk]][] = array_merge($e, array($c => &$m[$e[$k]]));
            }
            $final = array(
                    'wo_number'=>$r,
                    'leaf'=>false,
                    'children'=>$m[$r]
            );
            return $final;
    }

And this solved my problem!

Yoseph
  • 608
  • 9
  • 22
0

I has the same problem and its very simple resolved under javascript after data received by store extension for struct {DictDataArray: [{Id, parentId, Name}]}

appendRawData: function(data){
            var me = this;
            var rNode = me.getRootNode();
            data = (Ext.isString(data) && Ext.decode(data)) || data;
            function ParentFilter(element, index, array){
                return element.ParentId == this.filterId;
            };

            function BuildTree(list, filter){
                var tmp = list.filter(ParentFilter, filter);
                if(tmp && tmp.length)
                {
                    var node= (filter.filterId && me.getById(filter.filterId)) || rNode;
                    node && node.appendChild && node.appendChild(tmp);
                }
                for(var item in tmp){
                    BuildTree(list, {filterId: tmp[item].Id});
                    var node= me.getById(tmp[item].Id);
                    node.set('leaf', !node.hasChildNodes());
                }
            };
            data && data.DictDataResult && BuildTree(data.DictDataResult, {filterId: null});
        },

and after load simple call

store.appendRawData(response)

but other way is create own reader and represent data into loadData by overloaded this function that read raw data and transform it as you need