递归过滤/减少嵌套对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归过滤/减少嵌套对象相关的知识,希望对你有一定的参考价值。

我有一个深层嵌套的对象,我需要搜索删除某些键。要删除的键存储在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": []
            
        
答案

前段时间,我尝试使用新提议cloneObj()创建一个Object.fromEntries()方法来深度克隆对象。您可以在下一个链接上查看我在那个时刻提出的问题以供参考:Deep-Cloning an object using 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;
另一答案

它有点hacky并且性能不高,所以如果你处理的是非常大的对象图,它可能不是一个好的解决方案,但这里是使用replacer中的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);
另一答案

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

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; 
另一答案

您的代码中的错误是您在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);
另一答案

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

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

以上是关于递归过滤/减少嵌套对象的主要内容,如果未能解决你的问题,请参考以下文章

通过搜索嵌套对象属性过滤对象数组

如何过滤嵌套集合实体框架对象?

如何在 Angular 中为嵌套的 JSON 对象使用搜索过滤器?

过滤嵌套对象

返回根据嵌套对象过滤的 BigQuery 数据

基于嵌套值的数组过滤对象数组