【问题标题】:NodeJS : Loop through nested JSON and remove elements based on a conditionNodeJS:遍历嵌套的 JSON 并根据条件删除元素
【发布时间】:2019-08-18 08:59:24
【问题描述】:

假设我有一个下面的对象:

{
"schema": [
{
  "field": "name",
  "type": "String",
  "enabled": true
},
{
  "field": "age",
  "type": "Int",
  "enabled": false
},
{
  "field": "modelObj",
  "type": "object",
  "enabled": true,
  "stuff": [
    {
      "name": "mod1",
      "type": "array",
      "enabled": true
    },
    {
      "name": "mod2",
      "type": "String",
      "enabled": false
    },
    {
     "name": "mod3",
      "type": "array",
      "enabled": true
    }
  ]
},
{
  "name": "modelArr",
  "type": "array",
  "enabled": false,
  "elements": {
    "elementsType": "String"
  }
},
{
  "name": "modelsNestedArr",
  "type": "array",
  "enabled": true,
  "elements": {
    "elementsType": "object"
  },
  "stuff": [
    {
      "name": "name",
      "type": "String",
      "enabled": true
    },
    {
      "name": "models",
      "type": "array",
      "enabled": false,
      "elements": {
        "elementsType": "String"
      }
    }
  ]
  }
  ]
  }

我想递归地遍历这个对象,并根据“启用”是否为假,删除该项目。

所以预期的输出是:

[
{
  "field": "name",
  "type": "String",
  "enabled": true
},
{
  "field": "modelObj",
  "type": "object",
  "enabled": true,
  "stuff": [
    {
      "name": "mod1",
      "type": "array",
      "enabled": true
    },
    {
     "name": "mod3",
      "type": "array",
      "enabled": true
    }
  ]
},
{
  "name": "modelsNestedArr",
  "type": "array",
  "enabled": true,
  "elements": {
    "elementsType": "object"
  },
  "stuff": [
    {
      "name": "name",
      "type": "String",
      "enabled": true
    }
  ]
  }
 ]

我写的代码如下:

function r(a){
     for (i = a.length - 1; i >= 0; --i) {
        if(!a[i].enabled){
         a.splice(i,1)
        } else if (a[i].enabled){
            if(a[i].type == "object"){
              if(a[i]){
                a[i].stuff= r(a[i].stuff)
              }
        } else if (a[i].type == "array"){
            if(a[i].hasOwnProperty("elements") && a[i].elements.elementsType== "object"){
               a[i].stuff= r(a[i].stuff)
           }
        } 
      }
    }

    return a
    }

    var final = r(a.schema)
    console.log(JSON.stringify(final))

但是有了这个我得到以下输出:

错误输出:

[
{
"field": "name",
"type": "String",
"enabled": true
},
{
"field": "age",
"type": "Int",
"enabled": false
},
{
"field": "modelObj",
"type": "object",
"enabled": true,
"stuff": [
  {
    "name": "mod1",
    "type": "array",
    "enabled": true
  },
  {
    "name": "mod2",
    "type": "String",
    "enabled": false
  },
  {
    "name": "mod3",
    "type": "array",
    "enabled": true
  }
]
},
{
"name": "modelArr",
"type": "array",
"enabled": false,
"elements": {
  "elementsType": "String"
}
},
{
"name": "modelsNestedArr",
"type": "array",
"enabled": true,
"elements": {
  "elementsType": "object"
},
"stuff": [
  {
    "name": "name",
    "type": "String",
    "enabled": true
  }
]
}
]

我到底做错了什么?

【问题讨论】:

    标签: node.js json recursion nested iteration


    【解决方案1】:

    使用 .map() 和 .filter() 的递归函数将使您的生活更轻松:

      function isEnabledRecursive(data){
        return data.filter(
          item=>item.enabled
        )
        .map(
          item=>item.stuff?Object.assign(item,{stuff:isEnabledRecursive(item.stuff)})
          :item
        );
      }
    const dataWithFilteredSchema = Object.assign(data, {schema:isEnabledRecursive(data.schema)})
    

    这将适用于任何深度。

    它确实假设您的所有数组都在stuff 属性中。如果您有其他包含对象数组的属性,则必须使用 for(property of item) 之类的内容循环遍历每个属性,以查看它们中是否包含数组。

    至于为什么您的原始代码不起作用,您的 else if 语句中没有任何内容正在运行。当您说a[i].typea[i].dataType 时,它是未定义的。你可能是想说typeof a[i] === "object"typeof a[i] === "array"

    【讨论】:

    • 这对我帮助很大。我需要阅读地图并更好地过滤。谢谢!
    【解决方案2】:

    根据我的观察,您的架构对象或函数不一致。 您的架构对象同时具有 typedataType 键。我将所有这些都视为 type,这就是解决方案。

    通过您的拼接实现,数组在循环中会发生变化,并且可能会跳过一些索引。

    例如如果数组是,

    [
      {name: 'mod1', enabled: false},
      {name: 'mod2', enabled: true},
      {name: 'mod3', enabled: true}
    ]
    

    删除索引 0 后,数组长度变为 2 而不是 3。因此,将少访问一个元素。

    因此我包含了另一个对象 deleteIndices 来标记循环完成后要删除的索引。

    顺便说一句,您可以随时选择 ma​​pfilter 来制作这种方式 更简单。

    function r(a) {
      let deleteIndices = {};
      for (let i = 0; i < a.length; i++) {
        if (!a[i].enabled) {
          deleteIndices[i] = true;
        } else {
          if (a[i].type == "object") {
            a[i].stuff = r(a[i].stuff)
          } else if (a[i].type == "array" && a[i].elements && a[i].elements.elementsType == "object") {
            a[i].stuff = r(a[i].stuff)
          }
        }
      }
    
      a = a.filter((e, index) => !deleteIndices[index])
      return a;
    }
    
    schema = r(schema)
    

    【讨论】:

    • 对不起。修复了问题
    • 没问题。我想上面的例子已经足够理解这个错误了。
    【解决方案3】:

    让我们将你的数组命名为a(object.schema),然后可以这样得到结果:

    var result = a.filter(element => element.enabled).map(element => {
      if ((element.dataType == "object" || element.type == "array") && element.stuff && element.stuff.length > 0) {
        element.stuff = element.stuff.filter(st => st.enabled);
      }
    
      return element;
    });
    

    小心,有时你有 dataType 有时你有类型作为字段名。

    您应该使用 ma​​pfilter。 编写和使用非常简单。

    文档:mapfilter

    【讨论】:

    • nitpick:在示例中 a 是一个对象而不是一个数组。
    • 'a' 对应于 'object.schema'
    • 这将对提问者提供的数据起作用,但深度仅为 1。如果“stuff”中的项目有自己的“stuff”数组,那么它们将不会被过滤。不确定这对提问者是否重要。
    • “东西”可以是任何深度,嵌套在自身内部。
    • 另外,我已经解决了关于“类型”和“数据类型”的问题
    【解决方案4】:

    我能想到的最简单和最甜蜜的解决方案,适用于任何深度

    function r(arr){
      let filtered =  arr.filter((obj)=>{
        return obj.enabled;
      });
      return filtered.map((obj)=>{
        if(obj.stuff)
          obj.stuff = r(obj.stuff)
        return obj
      })
    }
    result = r(a.schema)
    console.log(JSON.stringify(result,null,2))

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-19
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 2014-04-12
      • 1970-01-01
      • 2017-04-08
      • 2011-11-29
      相关资源
      最近更新 更多