Wang Xiao
Wang Xiao

Reputation: 315

How to use a recursive filter all single JSON data?

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;
}

Upvotes: 0

Views: 663

Answers (2)

Thomas
Thomas

Reputation: 3593

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);

Upvotes: 1

Fabricator
Fabricator

Reputation: 12772

This version is more understandable. The function either

  • do nothing when it has no value attribute
  • merge with child when it has only one child
  • repeat for every child when there are multiple children

var data = [{
        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'
            }] 
        }]
    }];
    
function merge(node) {
  if (node.value == undefined) return;
  if (node.value.length == 1) {
    var child = node.value[0];
    node.name = child.name;
    if (child.id) node.id = child.id;
    if (child.value) node.value = child.value;
    else delete node.value;
    merge(node);
  } else {
    node.value.forEach(function(child) {
      merge(child);
    });
  }
}

$('#input').html(JSON.stringify(data));
data.forEach(function(node) { merge(node) });
$('#output').html(JSON.stringify(data));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Input: <div id="input"></div>
Output: <div id="output"></div>

Upvotes: 0

Related Questions