Группировка и иерархическое сопоставление с Lodash

#javascript #lodash

#javascript #Lodash

Вопрос:

Я пытаюсь преобразовать некоторые данные с помощью Lodash groupBy и map. Вот пример данных:

 var data = [
{name: 'x', qty: 0, rate: 10},
{name: 'x', qty: 10, rate: 2},
{name: 'y', qty: 5, rate: 20},
{name: 'y', qty: 55, rate: 11}]
  

Мне нужны эти данные в формате:

 var data = [
{name: 'x', pricing: [{qty: 0, rate: 10}, {qty: 10, rate: 2}]},
{name: 'y', pricing: [{qty: 5, rate: 20}, {qty: 55, rate: 11}]}]
  

Ниже приведена моя попытка:

 var m = _.chain(data)
.groupBy(data, 'name')
.map( function(i) {
  return {
    name: _.first(i).name,
    pricing: _.map(i, function(r) {
      return _.pick(r, ['qty', 'rate'])
    })
  }
})
  

Это приводит к

 [{
    "name": "x",
    "pricing": [
      {"qty": 0, "rate": 10},
      {"qty": 10, "rate": 2},
      {"qty": 5,"rate": 20},
      {"qty": 55,"rate": 11}]
}]
  

Я не смог понять, что я делаю не так. Может быть, это даже недопустимо, и есть способ получше?

Ответ №1:

Вам нужно сопоставить новый объект и получить выбранные значения.

 var data = [{ name: 'x', qty: 0, rate: 10 }, { name: 'x', qty: 10, rate: 2 }, { name: 'y', qty: 5, rate: 20 }, { name: 'y', qty: 55, rate: 11 }],
    result = _(data)
        .groupBy('name')
        .map((pricing, name) => ({
            name,
            pricing: _.map(pricing, _.partialRight(_.pick, ['qty', 'rate']))
        }))
        .value();

console.log(result);  
 .as-console-wrapper { max-height: 100% !important; top: 0; }  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>  

Ответ №2:

Вам не нужно использовать Lodash для этого — стандартные методы массива Javascript работают просто отлично. Сведите к объекту, проиндексированному name , затем получите значения этого объекта:

 var data = [
{name: 'x', qty: 0, rate: 10},
{name: 'x', qty: 10, rate: 2},
{name: 'y', qty: 5, rate: 20},
{name: 'y', qty: 55, rate: 11}]

const obj = data.reduce((a, { name, ...rest }) => {
  if (!a[name]) a[name] = { name, pricing: [] };
  a[name].pricing.push(rest);
  return a;
}, {});
const output = Object.values(obj);
console.log(output);  

Ответ №3:

Вы можете сделать это с помощью reduce()

 var data = [
{name: 'x', qty: 0, rate: 10},
{name: 'x', qty: 10, rate: 2},
{name: 'y', qty: 5, rate: 20},
{name: 'y', qty: 55, rate: 11}]

let res = data.reduce((ac,a) => {
  let i = ac.findIndex(x => x.name === a.name);
  if(i === -1) i = ac.push({name:a.name,pricing:[]}) - 1
  ac[i].pricing.push({qty:a.qty,rate:a.rate});
   return ac;
},[])
console.log(res);  

Ответ №4:

Ваш код на самом деле в порядке, за исключением небольшой ошибки. Вы начинаете цепочку с data , а затем пытаетесь сгруппировать с помощью data . Поскольку ни один из элементов не возвращает true для этого предиката, все они объединены в одну группу, и это является причиной вашего единого объекта.

Вам нужно изменить .groupBy(data, 'name') на .groupBy('name') .

Пример:

 var data = [{ name: 'x', qty: 0, rate: 10 }, { name: 'x', qty: 10, rate: 2 }, { name: 'y', qty: 5, rate: 20 }, { name: 'y', qty: 55, rate: 11 }];

var m = _.chain(data)
  .groupBy('name') // group by name, and not by data, 'name'
  .map(function(i) {
    return {
      name: _.first(i).name,
      pricing: _.map(i, function(r) {
        return _.pick(r, ['qty', 'rate'])
      })
    }
  })

console.log(m);  
 .as-console-wrapper {
  max-height: 100% !important;
  top: 0;
}  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>