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

时间:2018-03-27 17:26:05

标签: javascript lodash

我有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 名称的属性。我的目标是选择第一级的所有属性,因此id为43的对象的属性我选择了所有,并且从每个嵌套对象中,我只选择属性id和名称,而另一个将被丢弃。

我在项目lodash中使用,所以我找到了函数选择,但我不知道如何将这个函数用于嵌套对象。另请注意,嵌套对象也可以在数组中。请问有一些优雅的方式吗?谢谢你的建议。

2 个答案:

答案 0 :(得分:0)

只要您的对象确实结束了嵌套级别,您就可以使用递归来完成此操作。否则,您可能会导致堆栈限制错误。

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;
}

答案 1 :(得分:0)

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',
])

这是相关的摩卡测试:

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: []
    });
  });
});

请记住,没有对此进行性能测试。大物体可能会变慢。