简体   繁体   English

搜索并修改深度嵌套的 javascript 对象

[英]Search through and modify a deeply nested javascript object

I have an object that can be deeply nested with objects, arrays, arrays of objects, and so on.我有一个可以与对象、数组、对象数组等深度嵌套的对象。

Every nested object has a sys property which in turn has an id property.每个嵌套对象都有一个sys属性,而该属性又具有一个id属性。

I have a separate list of id values that correspond to objects that I want to remove from the original object.我有一个单独的id值列表,这些值对应于我想从原始对象中删除的对象。 How can I go about recursively looping through the entire object and modify it to no longer include these?如何递归遍历整个对象并将其修改为不再包含这些对象?

For example, say I have the following data:例如,假设我有以下数据:

let data = {
  sys: {
    id: '1'
  },
  items: [
    {
      sys: {
        id: '2'
      },
      fields: {
        title: 'Item title',
        sponsor: {
          sys: {
            id: '3'
          },
          fields: {
            title: 'Sponsor Title'
          }
        },
        actions: [
          {
            sys: {
              id: '4'
            },
            fields: {
              title: 'Google',
              url: 'google.com'
            }
          },
          {
            sys: {
              id: '5'
            },
            fields: {
              title: 'Yahoo',
              url: 'yahoo.com'
            }
          }
        ]
      }
    }
  ]
}

Then I have an array of id 's to remove:然后我有一个要删除的id数组:

const invalidIds = ['3', '5'];

After I run the function, the resulting object should have the property with sys.id of '3' set to null , and the object with sys.id of '5' should simply be removed from its containing array:在我运行该函数后,生成的对象应该具有sys.id'3'的属性设置为null ,并且sys.id'5'的对象应该简单地从其包含的数组中删除:

// Desired Output:
{
  sys: {
    id: '1'
  },
  items: [
    {
      sys: {
        id: '2'
      },
      fields: {
        title: 'Item title',
        sponsor: null,
        actions: [
          {
            sys: {
              id: '4'
            },
            fields: {
              title: 'Google',
              url: 'google.com'
            }
          }
        ]
      }
    }
  ]
}

With help from this solution , I'm able to recursively search through the object and its various nested arrays:此解决方案的帮助下,我能够递归搜索对象及其各种嵌套数组:

const process = (key, value) => {
  if (typeof value === 'object' && value.sys && value.sys.id && invalidIds.includes(value.sys.id)) {
    console.log('found one', value.sys.id);
  }
};

const traverse = (obj, func) => {
  for (let key in obj) {
    func.apply(this, [key, obj[key]]);

    if (obj[key] !== null) {
      if (typeof obj[key] === 'object') {
        traverse(obj[key], func);
      } else if (obj[key].constructor === Array) {
        obj[key].map(item => {
          if (typeof item === 'object') {
            traverse(item, func);
          }
        });
      }
    }
  }
};

traverse(data, process);

However I can't figure out how to properly modify the array.但是我不知道如何正确修改数组。 In addition, I'd prefer to create an entirely new object rather than modify the existing one in order to keep things immutable.此外,我更愿意创建一个全新的对象,而不是修改现有对象以保持事物不变。

Here are the observations that led to my solution:以下是导致我的解决方案的观察结果:

  1. To create a new object, you need to use return somewhere in your function.要创建新对象,您需要在函数中的某处使用return
  2. To remove items from array, you need to filter out valid items first, then recursively call traverse on them.要从数组中删除项目,您需要先过滤掉有效的项目,然后对它们递归调用traverse
  3. typeof obj[key] === 'object' will return true even for Array, so next else if block won't ever be hit. typeof obj[key] === 'object'即使对于 Array 也将返回 true,因此else if块永远不会被击中,则接下来的else if

As for implementation, my first step was to create a helper good function to detect invalid objects.至于实现,我的第一步是创建一个辅助good函数来检测无效对象。

good = (obj) =>{
    try{return !(invalidIds.includes(obj.sys.id));}
    catch(err){return true;}
}

Now the main traverse -现在主要的traverse -

traverse = (obj) => {
    //I assumed when an object doesn't have 'sys' but have 'id', it must be sys obj.
    if (obj==null) return null;
    if(obj.constructor === Array) return obj.filter(good).map(traverse);
    if(obj.sys==undefined) { //this checks if it's sys object.
        if(obj.id!=undefined) return obj;  
    }

    for (let key in obj) {
        if (key!=0) {
            if (good(obj[key])) {obj[key] = traverse(obj[key]);}
            else {obj[key] = null;}
        }
    }
        return obj;
};

In case of Array objects, as per point 2, I filtered out valid objects first, then mapped them to traverse.对于 Array 对象,根据第 2 点,我首先过滤掉有效对象,然后将它们映射到遍历。 In case of Objects, = operator was used to catch valid sub-objects, returned by recursive call to traverse , instead of simply modifying them.在对象的情况下, =运算符用于捕获有效的子对象,通过递归调用traverse返回,而不是简单地修改它们。

Note: I hardly know javascript, but took a go at it anyway because this recursive problem is fairly common.注意:我几乎不了解 javascript,但还是尝试了一下,因为这个递归问题相当普遍。 So watch out for JS specific issues.所以要注意 JS 的特定问题。 Specifically, as outlined in comment, I'm not content with how I checked for 'sys' objects.具体来说,如评论中所述,我对检查“sys”对象的方式并不满意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM