如何循环这个嵌套对象?

时间:2016-11-09 13:15:01

标签: javascript arrays

我有一个动态生成的JavaScript对象,它由嵌套对象和数组组成。我无法找到列出所有嵌套对象的正确方法,因为该特定对象是动态创建的。

这是对象:

var tagdata = {
  "Sentence": [{
      "NP": [{
          "NMP": "cat"
        }, {
          "NMP": "dog"
        }]
    }, {
      "VP": [{
          "KPD": "tree"
        }, {
          "NP": [{
              "NMP": "ball"
            }, {
              "NMP": "bat"
            }]
        },{
          "NP": [{
              "NMP": "ground"
            }, {
              "NMP": "time"
            }]
        }]
    }]
};

我需要的输出如下所示:

[{ key: 1, text: 'Sentence' },
 { key: 2, text: 'NP', parent: 1 },
 { key: 3, text: 'VP', parent: 1 },
 { key: 4, text: 'NMP', parent: 2 },
 { key: 5, text: 'NMP', parent: 2 },
 { key: 6, text: 'KPD', parent: 3 },
 { key: 7, text: 'NP', parent: 3 },
 { key: 8, text: 'NP', parent: 3 },
 { key: 9, text: 'cat', parent: 4 },
 { key: 10, text: 'dog', parent: 5 },
 { key: 11, text: 'tree', parent: 6 },
 { key: 12, text: 'NMP', parent: 7 },
 { key: 13, text: 'NMP', parent: 7 },
 { key: 14, text: 'NMP', parent: 8 },
 { key: 15, text: 'NMP', parent: 8 },
 { key: 16, text: 'ball', parent: 12},
 { key: 17, text: 'bat', parent: 13},
 { key: 18, text: 'ground', parent: 14},
 { key: 19, text: 'time', parent: 15},]

此数据将在树中使用,因此顺序可能不同,但应保持密钥:父关系。这是我尝试过的代码:

let newtags=[{key:1,text:'Sentence'}];
tagdata["Sentence"].map( (elem,x) => {    
  newtags.push({key:x,text:Object.keys(elem)[0],parent:x});
  if(Object.keys(elem)[0].length !== 0){
    var y=x+1;
    newtags.push({key:y,text:Object.values(elem)[0][x],parent:y});
  }    
});

console.log(newtags);

3 个答案:

答案 0 :(得分:2)

您的代码适用于第一级,但是当您尝试更深层次时,您将分配重复的值(y=x+1,当x具有该值时.map()方法的下一次迭代。

这里需要的是一个递归函数,它可以调用自己以与任何其他级别相同的方式处理嵌套级别。

您可以使用此ES6,函数式编程解决方案:

function listItems(obj) {
    var key = 0;
    return (function recurse(obj, parent = undefined) {
        return Object(obj) !== obj ? { key: ++key, text: obj, parent }
            :   Array.isArray(obj) ? Object.keys(obj).reduce( (acc, text) =>
                    acc.concat(recurse(obj[text], parent)), [])
            :   Object.keys(obj).reduce( (acc, text) =>
                    acc.concat({ key: ++key, text, parent },
                                recurse(obj[text], key)), []);
    })(obj);
}

// Sample data
var tagdata = {
  "Sentence": [{
      "NP": [{ "NMP": "cat" }, { "NMP": "dog" }]
    }, {
      "VP": [{
          "KPD": "tree"
        }, {
          "NP": [{ "NMP": "ball" }, { "NMP": "bat" }]
        },{
          "NP": [{ "NMP": "ground" }, { "NMP": "time" }]
        }]
    }]
};

// Extract the objects and output:
console.log(listItems(tagdata));
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 1 :(得分:1)

我认为这可以满足您的需求。它使用递归算法来处理与具有对象的情况分开的数组的情况。只是一个字符串的基本情况在processChild处理。

let state = [];

function processChild(child, parent) {
  if (Array.isArray(child)) {
    return processArray(child, parent);
  }

  if (typeof(child) == 'object') {
    return processObject(child, parent);
  }

  let tag = {
    key: state.length + 1,
    text: child,
  };
  if (parent) {
    tag.parent = parent;
  }
  state.push(tag);
}

function processObject(object, parent) {
  parent = parent || 0;

  let keys = Object.keys(object);
  for (let i = 0; i < keys.length; i++) {
    //console.log(keys[i]);
    let tagIndex = state.length + 1;
    let text = keys[i];
    let tag = {
      key: tagIndex,
      text: text,
    };
    if (parent) {
      tag.parent = parent;
    }

    state.push(tag);

    let child = object[keys[i]];
    processChild(child, tagIndex);
  }
}

function processArray(array, parent) {
  parent = parent || 0;
  for (let i = 0; i < array.length; i++) {
    //console.log(array[i]);
    let child = array[i];
    //console.log('Child', child);
    processChild(child, parent);
  }
}

function process(){
  let initialState = JSON.parse(input.innerHTML);
  processChild(initialState);
  code.innerHTML = JSON.stringify(state).replace(/},/g,'},\n');
}
#input{
  width: 100%;
  height: 200px;
}
<textarea id="input">
  {
  "Sentence": [{
    "NP": [{
      "NMP": "cat"
    }, {
      "NMP": "dog"
    }]
  }, {
    "VP": [{
      "KPD": "tree"
    }, {
      "NP": [{
        "NMP": "ball"
      }, {
        "NMP": "bat"
      }]
    }, {
      "NP": [{
        "NMP": "ground"
      }, {
        "NMP": "time"
      }]
    }]
  }]
}
</textarea>
<button onClick="process()">Process</button>
<pre id="code"></pre>

答案 2 :(得分:1)

只是为了好玩,一种非递归的方式。

  • 一个数组跟踪最终结果(results
  • 一个数组跟踪数据到进程
  • 当找到嵌套数据时,它被包装在&#34;待办事项&#34;并添加到todo数组
  • 重复直到没有项目

&#13;
&#13;
var tagData = [{Sentence:[{NP:[{NMP:"cat"},{NMP:"dog"}]},{VP:[{KPD:"tree"},{NP:[{NMP:"ball"},{NMP:"bat"}]},{NP:[{NMP:"ground"},{NMP:"time"}]}]}]}];

var isNode = n => Array.isArray(n);
var isLeaf = n => !isNode(n);
var todoItem = parent => node => ({ parent, node });

var flatten = function(arr) {
  var result = [],
      todo = arr.map(todoItem(0)),
      current, node, parent, key, innerNode;

  while (todo.length) {
    ({node, parent} = todo.pop());
    key = result.length + 1;

    Object.keys(node).forEach(k => {
      innerNode = node[k];
      result.push({ key, parent, text: k });

      if (isLeaf(innerNode)) {
        result.push({ 
          key: key + 1,
          parent: key,
          text: innerNode
        });
      } else {
        todo = todo.concat(
          innerNode.map(todoItem(key)));
      }
    });
  };

  return result;
};

// Print output
console.log(
  flatten(tagData)
  .map(o => [
    "key: " + (o.key < 10 ? " " : "") + o.key,
    "parent: " + (o.parent < 10 ? " " : "") + o.parent,
    o.text
  ].join(" | "))
);
&#13;
.as-console-wrapper {
  min-height: 100%;
}
&#13;
&#13;
&#13;