mike_hornbeck
mike_hornbeck

Reputation: 1622

Encode nested array to proper json without array indexes

I have a sample 2d $tasks array which describes a nested structure :

Array
(
    [14] => Array
        (
            [Id] => 14
            [parentId] => null
            [Name] => T1
        )

    [40] => Array
        (
            [Id] => 40
            [parentId] => null
            [Name] => T5
        )

    [41] => Array
        (
            [Id] => 41
            [parentId] => null
            [Name] => T4
        )

    [22] => Array
        (
            [Id] => 22
            [parentId] => 14
            [Name] => T2
        )

    [43] => Array
        (
            [Id] => 43
            [parentId] => 22
            [Name] => T2 child
        )

    [42] => Array
        (
            [Id] => 42
            [parentId] => 14
            [Name] => T3
        )

)

Using the code below I'm transforming this to a proper tree structure :

$sortedArray = array();
// get first level
foreach($tasks as $k => $v){
    if($v['parentId'] == 'null'){
        $sortedArray[$k] = $v;
        unset($tasks[$k]);
    }
}
// sort parents
asort($sortedArray);

function getChildren(array & $a1, array & $a2){
    foreach($a1 as $k => $v){
        findChildren($v, $a2, $k);      
    }
}

function findChildren($rec1, array & $a2, $key){

    foreach($a2 as $k => $v){
        if($rec1['parentId'] == $v['Id']){
            $a2[$k]['children'][$rec1['Id']] = $rec1;
            unset($tasks[$key]);
        } else {
            if (isset($v['children'])){
                findChildren($rec1, $a2[$k]['children'], $key);
            }
        }
    }
}

findChildren($tasks, $sortedArray);

And tho output $sortedArray after running this code looks as follows :

Array
(
    [14] => Array
        (
            [Id] => 14
            [parentId] => null
            [Name] => T1
            [children] => Array
                (
                    [22] => Array
                        (
                            [Id] => 22
                            [parentId] => 14
                            [Name] => T2
                            [children] => Array
                                (
                                    [43] => Array
                                        (
                                            [Id] => 43
                                            [parentId] => 22
                                            [Name] => T2 child
                                        )

                                )

                        )

                    [42] => Array
                        (
                            [Id] => 42
                            [parentId] => 14
                            [Name] => T3
                        )

                )

        )

    [40] => Array
        (
            [Id] => 40
            [parentId] => null
            [Name] => T5
        )

    [41] => Array
        (
            [Id] => 41
            [parentId] => null
            [Name] => T4
        )

)

The problem is, that after calling json_encode on this output array in it's current state I'm getting :

{"14":{"Id":"14","parentId":"null"...

so all nested arrays are inserted with their indexes. I know I can fix the first level using array_values. But is there any simple way of doing this for all levels ? Without it I end up with 'children' being not an array but object which is not satisfying for me.

Upvotes: 3

Views: 6612

Answers (3)

sirhacksalot
sirhacksalot

Reputation: 43

You did not include your json_encode call, if you happened to do it like this...

json_encode(array_values($sortedArray), JSON_FORCE_OBJECT);

then the force will put the numeric indexes in anyway. Not sure if this will help you but it might help someone else who's seeing the same problem.

Upvotes: 2

Elliot B.
Elliot B.

Reputation: 17681

The code isn't present in your post, but $tasks was created as an associative array. In your example you also iterate through $tasks as you would an associative array:

foreach($tasks as $k => $v){
    ...
}

You need to add children to $tasks as you would with a numeric array; the difference is this:

//associative array
$test = array();
$test["43"] = "hello";
$test["40"] = "hello1";
$test["23"] = "hello2";
print_r($test);

//numeric array
$testb = array(); 
$testb[] = "hello";
$testb[] = "hello1";
$testb[] = "hello2";
print_r($testb);

Live example: http://codepad.org/tsOhX88h

With the numeric array, the top level index (e.g., 14) you've cited as the problem {"14":{"Id":"14","parentId":"null"... is no longer present.

As a simple final step, use this code to push the items from the associative array on to a new numeric array:

$finalArray = array();
foreach ($sortedArray as $key=>$val ){
    $finalArray[] = $sortedArray[$key];
}
print_r($finalArray);

Live example: http://codepad.org/uSGSr1DC

Or you could do it in one shot with array_values:

$finalArray = array();
$finalArray = array_values($sortedArray);
print_r($finalArray);

Live example: http://codepad.org/D7uBSRr8

Upvotes: 7

mike_hornbeck
mike_hornbeck

Reputation: 1622

Ok, so I was able to come up with something like this :

function makeIndexed(array & $arr, array & $par = null){
    if(is_null($par)){
        $arr = array_values($arr);
    } else {
        $par['children'] = array_values($arr);
    }

    for($i=0; $i<count($arr); $i++) {   
        $temp = @$arr[$i]['children'];
        if(isset($temp)) {
            makeIndexed($arr[$i]['children'], $arr[$i]);
        }       
    }
}

And now calling makeIndexed($sortedArray); gives me a valid JSON, where 'children' is an array of nodes and not an object. I'm aware of the fact that this code is probably ugly and lacks performance, but at least generates the proper result. If anyone can create something cleaner/shorter I'd gladly mark this as the correct answer.

Upvotes: 0

Related Questions