Loda sh выбор из глубокого массива - PullRequest
1 голос
/ 17 января 2020

У меня есть сложный Объект

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

и здесь песочница для игры с

Ответы [ 3 ]

2 голосов
/ 18 января 2020

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

0 голосов
/ 18 января 2020

Я обновил этот ответ, добавив в него специальную функцию, написанную мной, которая решает проблему. Я не проверял его на всех возможных сценариях, но я уверен на 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>
0 голосов
/ 17 января 2020

Loada sh альтернатива

Idk о loada sh, но я бы просто удалил [] из ваших строковых ключей и использовал бы простую функцию, чтобы получить то, что вы есть ищу.

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