【问题标题】:Lodash pick from deep arrayLodash 从深度数组中挑选
【发布时间】:2020-01-17 15:43:16
【问题描述】:

我有一个复杂的对象

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

我有钥匙列表:

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

我想要做的 - 是选择我有键的所有值。问题是 - 我不确定如何处理数组 ("c.d[].foo")。不知道数组有多长,哪些元素有没有foo

结果应该是

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

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;
    }, {});
}

here a sandbox to play with

【问题讨论】:

    标签: javascript arrays object lodash


    【解决方案1】:

    map-factory 可能有助于以优雅的方式完成这项任务。更多详情请看这里:https://www.npmjs.com/package/map-factory

    代码如下所示

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

    【讨论】:

      【解决方案2】:

      Loadash 替代方案

      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',
        'c.d[].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);

      【讨论】:

      • 谢谢!我想过——但在实施某些事情之前,我想知道是否有更优雅的方式来实现我的目标。 PS。结果并不完全符合预期,因为您只是获取值而不是选择它们 - 在这里查看更多选择 lodash.com/docs/4.17.15#pick
      • 我已经编辑了 sn-p:我已经处理了以下情况:没有数据/当您选择其中的最后一个可用键时从数组中删除该项目
      【解决方案3】:

      我更新了这个答案,加入了一个我写的可以解决问题的特殊函数。我没有针对所有可能的情况对其进行测试,但我 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",
        "c.d[].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>

      【讨论】:

      • "c.d[].foo" 怎么样?这是问题的主要部分
      • 我更新了代码以包含一个解决 OP 所述问题的函数。谢谢你让我诚实。
      • 谢谢 - 这几乎是我需要的 - {} 不应该出现
      • @Senyaak => 我更新了函数以包含对您报告的问题的修复。它现在似乎可以工作了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-01
      • 1970-01-01
      相关资源
      最近更新 更多