Ad

How To Use A Recursive Filter All Single JSON Data?

- 1 answer

I want all of the individual nodes in the tree all merged into its parent, but if its child nodes contain two or more child nodes, without any change。

Here is my data model example:

[{
        name: "HOME",
        value: [{//Only node merged into the parent level
            name: "HOME",
            value: [{//Only node merged into the parent level
                name: "HOME",
                id: '1000'
            }] 
        }]
    }, {
        name: "ARTICLE",
        value: [{
            name: "ARTICLE",
            value: [{
                name: "ARTICLE TYPE 1",
                id: '2001'
            },{
                name: "ARTICLE TYPE 2",
                id: '2002'
            }] 
        },{
            name: "ARTICLE",
            value: [{//Only node merged into the parent level
                name: "ARTICLE TYPE 3",
                id: '2003'
            }] 
        }]
    }]

I want to filter the data get like this:

    [{
        name: "HOME",
        id: 1000
    }, {
        name: "ARTICLE",
        value: [{
            name: "ARTICLE",
            value: [{
                name: "ARTICLE TYPE 1",
                id: '2001'
            },{
                name: "ARTICLE TYPE 2",
                id: '2002'
            }] 
        },{
            name: "ARTICLE TYPE 3",
            id: '2003'
        }]
    }]

//Update 1: This is the idea, but now there is a problem, find the node can not fall back to the original node, you can only modify the current parent node:

function filter(data){
    for(var i = 0; i < data.length; i++){
        if( !data[i].value ) continue;

        //Check whether there are child nodes "value" is because it contains a "value" does not exist "id",
        //you must enter a recursive make the following checks
        if( data[i].value.length === 1 && !data[i].value[0].value ) {
            data[i].id = data[i].value[0].id;
            delete data[i].value;
            continue;
        }
        filter( data[i].value );
    }
    return data;
}

I am now directly modify the original object, I do not know whether it is reasonable to do so.

//Update 2: My final wording is like this, the resulting output appears to be correct, but are not sure the logic is correct, and it looks very ugly, or do not know whether there is a better solution?

function filter(data, parent){
    for(var i = 0; i < data.length; i++){
        if( data[i].value ) filter( data[i].value, data[i] );

        if( parent && data.length === 1 && !data[i].value ) {
            parent.id = data[i].id;
            delete parent.value;
        }
    }
    return data;
}
Ad

Answer

a simple version

function merge(node){
    if(node.value){
        var children = node.value.map(merge);
        return children.length === 1?
            children[0]:
            {
                name: node.name,
                value: children
            };
    }
    return node;
}
var result = data.map(merge);

or so:

function cp(a, b){
    for(var k in b){
        if(k === "value" || k in a) continue;
        a[k] = b[k];
    }
    return a;
}

function merge(node){
    if(node.value){
        var children = node.value.map(merge);
        return children.length===1 && !("value" in children[0])?
            cp(children[0], node):
            cp({ value: children }, node);
    }
    return cp({}, node);
}
var result = data.map(merge);
Ad
source: stackoverflow.com
Ad