从嵌套对象中选择特定属性

4

我有一些 JavaScript 对象,看起来类似于这样:

{
  id: 43,
  name: 'ajajaj'
  nestedObj1: {
    id: 53,
    name: 'ababab'
    foo: 'xxxx'
    nestedObj2: {
      id: 90,
      name: 'akakaka'
      foo2: 'sdsddd'
      surname: 'sdadasd'
      nestedObj3: {
        id: ..
        name: ..
        ...
      },
      objectsArr: [
        {id: .., name: ..., nestedOb4: {...} },
        {id: .., name: ..., nestedOb4: {...} }
      ]
    },
    foo0: ...
  }
  name: 'blabla'
}

我有许多对象,每个对象都有属性“id”和“name”。我的目标是选择第一级中的所有属性,因此我会选择具有id 43的对象的所有属性,并且从每个嵌套对象中,我只选择id和name属性,其他属性将被舍弃。
在项目中,我使用了lodash库,所以我找到了函数pick,但我不知道如何用于嵌套对象。请注意,嵌套对象也可以存在于数组中。是否有更优雅的方法?谢谢您的帮助。
2个回答

8
lodash 的 pick 函数可以获取嵌套的属性,例如:
_.pick(obj, [
  'id',
  'name',
  'nestedObj1.id',
  'nestedObj1.name',
  'nestedObj1.nestedObj2.id',
  'nestedObj1.nestedObj2.name',
  'nestedObj1.nestedObj2.nestedObj3.id',
  'nestedObj1.nestedObj2.nestedObj3.name',
  'nestedObj1.nestedObj2.objectsArr[0].id',
  'nestedObj1.nestedObj2.objectsArr[0].name',
  'nestedObj1.nestedObj2.objectsArr[0].nestedObj4.id',
  'nestedObj1.nestedObj2.objectsArr[0].nestedObj4.name',
  'nestedObj1.nestedObj2.objectsArr[1].id',
  'nestedObj1.nestedObj2.objectsArr[1].name',
  'nestedObj1.nestedObj2.objectsArr[1].nestedObj4.id',
  'nestedObj1.nestedObj2.objectsArr[1].nestedObj4.name',
])

问题在于,您必须知道数组的长度才能选择其中的所有对象。

为了缓解这个问题,您可以使用此函数来遍历数组:

const _ = require('lodash')

const pickExtended = (object, paths) => {
  return paths.reduce((result, path) => {
    if (path.includes("[].")) {
      const [collectionPath, itemPath] = path.split(/\[]\.(.+)/);
      const collection = _.get(object, collectionPath);

      if (!_.isArray(collection)) {
        return result;
      }

      const partialResult = {};
      _.set(
        partialResult,
        collectionPath,
        _.map(collection, item => pickExtended(item, [itemPath]))
      );

      return _.merge(result, partialResult);
    }

    return _.merge(result, _.pick(object, [path]));
  }, {});
};

您可以使用以下代码获取所需结果:
pickExtended(obj, [
  'id',
  'name',
  'nestedObj1.id',
  'nestedObj1.name',
  'nestedObj1.nestedObj2.id',
  'nestedObj1.nestedObj2.name',
  'nestedObj1.nestedObj2.nestedObj3.id',
  'nestedObj1.nestedObj2.nestedObj3.name',
  'nestedObj1.nestedObj2.objectsArr[].id',
  'nestedObj1.nestedObj2.objectsArr[].name',
  'nestedObj1.nestedObj2.objectsArr[].nestedObj4.id',
  'nestedObj1.nestedObj2.objectsArr[].nestedObj4.name',
])

下面是相关的mocha测试:

describe("pickExtended", () => {
  const object = {
    a: 1,
    b: "2",
    c: { d: 3, e: 4 },
    f: [
      { a: 11, b: 12, c: [{ d: 21, e: 22 }, { d: 31, e: 32 }] },
      { a: 13, b: 14, c: [{ d: 23, e: 24 }, { d: 33, e: 34 }] }
    ],
    g: [],
    h: [{ a: 41 }, { a: 42 }, { a: 43 }]
  };

  it("should pick properties in nested collection", () => {
    expect(
      pickExtended(object, ["a", "c.d", "f[].c[].d", "g[].a", "b[].a", "h[]"])
    ).to.deep.equal({
      a: 1,
      c: { d: 3 },
      f: [{ c: [{ d: 21 }, { d: 31 }] }, { c: [{ d: 23 }, { d: 33 }] }],
      g: []
    });
  });
});

请注意,此内容未进行性能测试。对于大型对象,可能会变得较慢。


1
你可以使用递归来实现,只要你的对象有嵌套层级的结束。否则,你就会冒着导致栈限制错误的风险。
function pickProperties(obj) {
    var retObj = {};
    Object.keys(obj).forEach(function (key) {
        if (key === 'id' || key === 'name') {
            retObj[key] = obj[key];
        } else if (_.isObject(obj[key])) {
            retObj[key] = pickProperties(obj[key]);
        } else if (_.isArray(obj[key])) {
            retObj[key] = obj[key].map(function(arrayObj) {
                return pickProperties(arrayObj);
            });
        }
    });
    return retObj;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接