繁体   English   中英

递归过滤/减少嵌套对象

[英]Filter / reduce nested object recursively

我有一个深层嵌套的对象,我需要搜索删除某些键。 要删除的键存储在removeKeys数组中指示的数组中。 目前,该函数仅过滤顶级对象,但对其余部分进行精细缩放,它只是不过滤子对象。 如何正确地减少完整对象以获得所需的输出?

最初未经过滤的对象:

let item = {
            "label": "test",
            "id": "test",
            "styles": {
                "label": "Styles",
                "styles": {
                    "test": {
                        "test": "test",
                        "label": "test",
                        "test1": {
                            "label": "test",
                            "image": {
                                "label": "test",
                                "type": "test",
                                "value": "test",
                                "autoSelect": "",
                                "id": ""
                            }
                        }
                    }
                }
            },
            "test": {
                "label": "test",
                "test": []
            }
        }

从对象中删除的键:

const removeKeys = ["label", "type", "autoSelect"];

用于过滤嵌套对象的递归函数:

let filterObject = filterNestObject(item);

function filterNestObject(item) {
  return Object.keys(item)
  .filter(key => {
    if (typeof item[key] === 'object') filterNestObject(item[key]);

    if (!removeKeys.includes(key)) return true;

    return false 
  })  
  .reduce((object, key) => {
    return {
      ...object,
      [key]: item[key]
    };
  }, {});

}

预期结果将是:

{
            "id": "test",
            "styles": {
                "styles": {
                    "test": {
                        "test": "test",
                        "test1": {
                            "image": {
                                "value": "test",
                                "id": ""
                            }
                        }
                    }
                }
            },
            "test": {
                "test": []
            }
        }

它有点hacky并且性能不高,所以如果你处理的是非常大的对象图,它可能不是一个好的解决方案,但这里是一个使用JSON.stringifyreplacer回调的JSON.stringify解决方案:

JSON.parse(JSON.stringify(audience, (k, v) => removeKeys.includes(k) ? undefined : v));

演示:

 let audience = { "label": "test", "id": "test", "styles": { "label": "Styles", "styles": { "test": { "test": "test", "label": "test", "test1": { "label": "test", "image": { "label": "test", "type": "test", "value": "test", "autoSelect": "", "id": "" } } } } }, "test": { "label": "test", "test": [] } } const removeKeys = ["label", "type", "autoSelect"]; let newAudience = JSON.parse(JSON.stringify(audience, (k, v) => removeKeys.includes(k) ? undefined : v)); console.log(newAudience); 

同样,如果您从JSON字符串解析原始对象,则可以使用来自JSON.parsereviver回调:

 let jsonString = `{ "label": "test", "id": "test", "styles": { "label": "Styles", "styles": { "test": { "test": "test", "label": "test", "test1": { "label": "test", "image": { "label": "test", "type": "test", "value": "test", "autoSelect": "", "id": "" } } } } }, "test": { "label": "test", "test": [] } }` const removeKeys = ["label", "type", "autoSelect"]; const audience = JSON.parse(jsonString, (k, v) => removeKeys.includes(k) ? undefined : v); console.log(audience); 

您的代码中的错误是您在filter回调中执行递归调用。 但是你丢失了递归调用返回的对象。 而是在reduce回调中创建它。

一个小的修正:为了测试一个值是否是一个对象,它不足以使typeof item[key] === "object"null也会通过该测试。 这是改编的代码:

 function filterNestObject(item) { return Object.keys(item) .filter(key => !removeKeys.includes(key)) .reduce((acc, key) => { return Object.assign(acc, { [key]: Object(item[key]) === item[key] ? filterNestObject(item[key]) : item[key] }); }, Array.isArray(item) ? [] : {}); } const item = {"label": "test","id": "test","styles": {"label": "Styles","styles": {"test": {"test": "test","label": "test","test1": {"label": "test","image": {"label": "test","type": "test","value": "test","autoSelect": "","id": ""}}}}},"test": {"label": "test","test": []}}; const removeKeys = ["label", "type", "autoSelect"]; const filterObject = filterNestObject(item); console.log(filterObject); 

您可以通过采用迭代和递归方法过滤键并构建新对象。

 function remove(object, keys) { return Object.assign({}, ...Object.keys(object) .filter(k => !keys.includes(k)) .map(k => ({ [k]: object[k] && typeof object[k] === 'object' ? remove(object[k], keys) : object[k] })) ); } var item = { label: "test", id: "test", styles: { label: "Styles", styles: { test: { test: "test", label: "test", test1: { label: "test", image: { label: "test", type: "test", value: "test", autoSelect: "", id: "" } } } } }, test: { label: "test", test: [] } }, removeKeys = ["label", "type", "autoSelect"]; console.log(remove(item, removeKeys)); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

你以递归方式调用函数,但是你没有对这个递归调用返回的结果做任何事情。 您必须使用筛选值覆盖子键:

 let item = { "label": "test", "id": "test", "styles": { "label": "Styles", "styles": { "test": { "test": "test", "label": "test", "test1": { "label": "test", "image": { "label": "test", "type": "test", "value": "test", "autoSelect": "", "id": "" } } } } }, "test": { "label": "test", "test": [] } } const removeKeys = ["label", "type", "autoSelect"]; let filterObject = filterNestObject(item); function filterNestObject(item) { return Object.keys(item) .filter(key => { if (typeof item[key] === 'object') { // set the key to the filtered result returned by the recursively called function item[key] = filterNestObject(item[key]); } if (!removeKeys.includes(key)) return true; return false }) .reduce((object, key) => { return { ...object, [key]: item[key] }; }, {}); } console.log(filterNestObject(item)); 

我可能会使用Object.entriesfilter + includesmapObject.fromEntries -

const removeDeepKeys = (keys = [], o = {}) =>
  Object (o) === o
    ? Object
        .fromEntries
          ( Object
              .entries (o)
              .filter (([ k, _ ]) => ! keys .includes (k))
              .map (([ k, v ]) => [ k, removeDeepKeys (keys, v) ])
          )
    : o

在你的item上尝试 -

removeDeepKeys ([ 'label', 'type', 'autoSelect' ], item)

输出 -

{
  "id": "test",
  "styles": {
    "styles": {
      "test": {
        "test": "test",
        "test1": {
          "image": {
            "value": "test",
            "id": ""
          }
        }
      }
    }
  },
  "test": {
    "test": {}
  }
}

编辑支持数组 -

const removeDeepKeys = (keys = [], o = {}) =>
  Array .isArray (o)
    ? o .map (v => removeKeys (keys, v))
    : Object (o) === o
        ? Object
            .fromEntries
              ( Object
                  .entries (o)
                  .filter (([ k, _ ]) => ! keys .includes (k))
                  .map (([ k, v ]) => [ k, removeDeepKeys (keys, v) ])
              )
        : o

前段时间,我尝试使用新建议Object.fromEntries()创建cloneObj()方法来深度克隆对象。 您可以在下一个链接上查看我在那一刻所做的问题以供参考: 使用Object.fromEntries()深度克隆对象

我相信这种方法可以稍加修改,以满足您的目标:

 const item = {"label": "test","id": "test","styles": {"label": "Styles","styles": {"test": {"test": "test","label": "test","test1": {"label": "test","image": {"label": "test","type": "test","value": "test","autoSelect": "","id": ""}}}}},"test": {"label": "test","test": [{label: "foo", test: "test4"}]}}; const removeKeys = ["label", "type", "autoSelect"]; const cloneObjWithoutKeys = (obj, keys) => { if (Object(obj) !== obj) return obj; else if (Array.isArray(obj)) return obj.map(o => cloneObjWithoutKeys(o, keys)); return Object.fromEntries( Object.entries(obj) .filter(([k, v]) => !keys.includes(k)) .map(([k, v]) => ([k, cloneObjWithoutKeys(v, keys)]) )); } console.log(cloneObjWithoutKeys(item, removeKeys)); 
 .as-console {background-color:black !important; color:lime;} .as-console-wrapper {max-height:100% !important; top:0;} 

注意,这也将遍历内部对象数组以过滤掉所需的键。

暂无
暂无

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

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