Фильтровать / уменьшать вложенный объект рекурсивно - PullRequest
1 голос
/ 25 апреля 2019

У меня есть глубоко вложенный объект, который мне нужно искать, чтобы удалить определенные ключи.Ключи для удаления хранятся в массиве, указанном в массиве 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": []
            }
        }

Ответы [ 6 ]

1 голос
/ 25 апреля 2019

Вы можете фильтровать ключи и создавать новые объекты, используя итеративный и рекурсивный подход.

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; }
1 голос
/ 25 апреля 2019

Ошибка в вашем коде в том, что вы делаете рекурсивный вызов в обратном вызове 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);
1 голос
/ 25 апреля 2019

Это немного хакерский и не очень производительный, так что это может быть не очень хорошим решением, если вы имеете дело с очень большими графами объектов, но вот решение с одной строкой, использующее обратный вызов 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);

Аналогичным образом, если вы анализируете исходный объект из строки JSON, вы можете использовать обратный вызов reviver из JSON.parse:

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);
0 голосов
/ 26 апреля 2019

Некоторое время назад я попытался создать метод cloneObj() для глубокого клонирования объекта с использованием нового предложения Object.fromEntries () .Вы можете проверить, для справки, вопрос, который я задал в тот момент, по следующей ссылке: Глубокое клонирование объекта с использованием 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;}

Обратите внимание, что при этом также будут проходить внутренние массивы объектов, чтобы отфильтровать нужные ключи.

0 голосов
/ 25 апреля 2019

Я бы, вероятно, использовал Object.entries, filter + includes, map и Object.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
0 голосов
/ 25 апреля 2019

Вы вызываете функцию рекурсивно, но вы ничего не делаете с результатом, который возвращает этот рекурсивный вызов.Вы должны перезаписать подраздел с отфильтрованным значением:

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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...