简体   繁体   English

Lodash 从深度数组中选取

[英]Lodash pick from deep array

I have an complex Object我有一个复杂的对象

{
  "a": 1,
  "b": {"test": {
    "b1": 'b1'
  }},
  "c": {
    "d": [{foo: 1}, {foo: 2}, {foo: 3, bar: 1}, {bar: 12}]
  },
}

And I have list of keys:我有钥匙列表:

[
  "a", 
  "b.test.b1",
  "c.d[].foo"
]

What I want to do - is to pick all values I have keys for.我想要做的 - 是选择我有键的所有值。 The problem is - I do not sure how to handle arrays ( "cd[].foo" ).问题是 - 我不知道如何处理数组( "cd[].foo" )。 I do not know how long the array is and which elements do or do not have foo我不知道数组有多长以及哪些元素有或没有foo

The result should be结果应该是

{
  "a": 1,
  "b": {"test": {
    "b1": 'b1'
  }},
  "c": {
    "d": [{foo: 1}, {foo: 2}, {foo: 3}]
  },
}

UPD If someone interested, here is my implementation of this function: UPD如果有人感兴趣,这是我对这个函数的实现:

const deepPick = (input, paths) => {

    return paths.reduce((result, path) => {
      if(path.indexOf('[]') !== -1) {
        if(path.match(/\[\]/g).length !== 1) {
          throw new Error(`Multiplie [] is not supported!`);
        }
        const [head, tail] = path.split('[]');

        const array = (get(input, head) || []).reduce((result, item) => {
          // if tail is an empty string, we have to return the head value;
          if(tail === '') {
            return get(input, head);
          }
          const value = get(item, tail);

          if(!isNil(value)) {
            result.push(set({} , tail, value));
          } else {
            result.push(undefined);
          }
          return result;
        }, []);

        const existingArray = get(result, head);

        if((existingArray || []).length > 0) {
          existingArray.forEach((_, i) => {
            if(!isNil(get(array[i], tail))) {
              set(existingArray, `${i}.${tail}`, get(array[i], tail));
            }
          });
        } else if(array.length > 0) {
          set(result, head, array);
        }
      } else {
        set(result, path, get(input, path));
      }
      return result;
    }, {});
}

and here a sandbox to play with 这里有一个沙箱可以玩

map-factory might help to do this task in elegant way. map-factory可能有助于以优雅的方式完成此任务。 see here for more details: https://www.npmjs.com/package/map-factory有关更多详细信息,请参见此处: https : //www.npmjs.com/package/map-factory

code will looks like this代码看起来像这样

const mapper = require("map-factory")();
mapper
  .map("a")
  .map("b.test.b1")
  .map("c.d[].foo");

const input = {
  a: 1,
  b: {
    test: {
      b1: "b1"
    }
  },
  c: {
    d: [{ foo: 1 }, { foo: 2 }, { foo: 3, bar: 1 }, { bar: 12 }]
  }
};

console.log(JSON.stringify(mapper.execute(input)));

Loadash alternative负载替代

Idk about loadash, but I would simply remove the [] from your string keys and use a simple function to retrieve what you are looking for. Idk 关于 loadash,但我会简单地从您的字符串键中删除[]并使用一个简单的函数来检索您要查找的内容。

 const obj = { a: 1, b: { test: { b1: 'b1', }, }, c: { d: [{ foo: 1, }, { foo: 2, }, { foo: 3, bar: 1, }, { bar: 12, }], }, }; const myKeys = [ 'a', 'b.test.b1', 'cd[].foo', ].map(x => x.replace(/\\[\\]/, '')); function deepSearch(key, obj) { // We split the keys so we can loop on them const splittedKeys = key.split('.'); return splittedKeys.reduce((tmp, x, xi) => { if (tmp === void 0) { return tmp; } if (tmp instanceof Array) { const dataIndex = tmp.findIndex(y => y[x] !== void 0); // If the data we are looking for doesn't exists if (dataIndex === -1) { return void 0; } const data = tmp[dataIndex]; const ptr = data[x]; // Remove the data only if it's the last key we were looking for if (splittedKeys.length === xi + 1) { delete data[x]; // If the array element we removed the key from is now empty // remove it if (Object.keys(data).length === 0) { tmp.splice(dataIndex, 1); } } return ptr; } const ptr = tmp[x]; // Remove the data only if it's the last key we were looking for if (splittedKeys.length === xi + 1) { delete tmp[x]; } return ptr; }, obj); } console.log('Results', myKeys.map(x => deepSearch(x, obj))); console.log('Final object', obj);

I updated this answer to include a special function that I wrote that solves the problem.我更新了这个答案以包含我编写的解决问题的特殊函数。 I haven't tested it against every possible scenario, but I know with 100% certainty that it runs for your cases.我没有针对所有可能的情况对其进行测试,但我 100% 确定它适用于您的情况。

 _.mixin({ "pickSpecial": function pickSpecial(obj, key) { if (!_.includes(key, '[]')) { return _.pick(obj, key); } else { const keys = _.split(key, /(\\[\\]\\.|\\[\\])/); const pickFromArray = _.chain(obj) .get(_.first(keys)) .map((nextArrayElement) => pickSpecial(nextArrayElement, _.reduce(_.slice(keys, 2), (curr, next) => `${curr}${next}`, ''))) .compact() .reject((elem) => (_.isObject(elem) || _.isArray(elem)) && _.isEmpty(elem)) .value(); return _.chain(obj) .pick(_.first(keys)) .set(_.first(keys), pickFromArray) .value(); } } }); const initialData = { "a": 1, "b": {"test": { "b1": 'b1' }}, "c": { "d": [{foo: 1}, {foo: 2}, {foo: 3, bar: 1}, {bar: 12}] }, }; const keys = [ "a", "b.test.b1", "cd[].foo" ]; /* Expected result { "a": 1, "b": {"test": { "b1": 'b1' }}, "c": { "d": [{foo: 1}, {foo: 2}, {foo: 3}] }, } */ const output = _.chain(keys) .map((key) => _.pickSpecial(initialData, key)) .reduce((obj, next) => _.merge({}, obj, next), {}) .value(); console.log(output);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

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

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