Есть ли простой способ отобразить вложенные данные с помощью Lodash?

Для моего текущего проекта я работаю с API, который возвращает данные в следующем формате:

{
  groups: [
    {
      items: [
        {
          points: [
            { name: "name1", ... },
            { name: "name2", ... },
            { name: "name3", ... },
            ...
          ],
          ...
        },
        ...
      ]
    },
    ...
  ],
  ...
};

Я хотел бы создать чистую функцию, mapValues, который принимает объект в вышеуказанном формате, а также объект, отображающий каждый name к value и возвращает ту же структуру, но с каждым point содержащий value что соответствует его name,

Например, позвонив mapValues(data, { name1: "value1", name2: "value2", name3: "value3" }) должен вернуть это:

{
  groups: [
    {
      items: [
        {
          points: [
            { name: "name1", value: "value1", ... },
            { name: "name2", value: "value2", ... },
            { name: "name3", value: "value3", ... },
            ...
          ],
          ...
        },
        ...
      ]
    },
    ...
  ],
  ...
};

Вот мой первый проход:

function mapValues(data, values) {
  return _.extend({}, data, {
    groups: _.map(ui.groups, (group) => {
      return _.extend({}, group, {
        items: _.map(group.items, (item) => {
          return _.extend({}, item, {
            points: _.map(item.points, (point) => {
              return _.extend({ value: values[point.name] }, point);
            })
          });
        })
      });
    })
  });
}

Это работает, но есть довольно много вложений дубликата кода. Для второй попытки я потянулся за рекурсией.

function mapValues(data, values) {
  return (function recursiveMap(object, attributes) {
    if (attributes.length === 0) { return _.extend({ value: values[object.name] }, object); }
    let key = attributes[0];
    return _.extend({}, object, {
      [key]: _.map(object[key], child => recursiveMap(child, attributes.slice(1)))
    });
  })(ui, ["groups", "items", "points"]);
}

Это тоже работает, но трудно читать и не очень кратко.

Есть ли более чистый способ рекурсивного сопоставления объекта с помощью Lodash? В идеале я ищу функциональный подход.

2 ответа

Вот способ, которым вы можете сделать это, используя Object.assign и никаких необычных функций

var data = <your data here>;
var values = <your mapped values>;

data.groups.items.points.map(p=>
  Object.assign(p, {value: values[p.name]})
);

Это работает, потому что массивы и объекты передаются по ссылке. Таким образом, любые изменения значений приведут к изменению оригинала.

Если вы не хотите изменять исходный набор данных, вам необходимо использовать {} в качестве первого аргумента (для назначения новому, пустому объекту) и показывать четкие пути чтения / записи для каждого объекта.

var newData = Object.assign({}, data,
  {groups:
    {items:
      {points: data.groups.items.points.map(p=>
        Object.assign({}, p, {value: values[p.name]})
      )}
    }
  }
);

Я знаю, что вы хотели Lodash, но у меня была такая же проблема некоторое время назад, и я придумал этот JSFiddle, я использую отличный инструмент, созданный nervgh. Это очень просто, но полезно. Вы можете настроить эту функцию, чтобы быть чистой, используя Object.assign, примите ключевой параметр и настройте его так, как хотите. Вы поняли идею.

function mapValues(data, values) {
  var iterator = new RecursiveIterator(data);
  for(let {node} of iterator) {
    if(node.hasOwnProperty('name')) {
        node.value = values[node.name];
    }
  }
  return data;
}
Другие вопросы по тегам