Отключение и сокращение структур данных в javascript

У меня есть набор данных о винах. Для каждой этикетки вина у меня есть несколько сведений, таких как страна происхождения, виноград, из которого оно сделано, белое или красное вино, его рейтинг и т. д. Я получаю эти данные из csv. В конечном счете, для визуализации, которую я создаю, мне нужны данные типа: Grape | Белый или красный | Считает. Мне удалось получить такие данные:

count: 1
grape: (3) ["Pinot Noir", "Malbec", "Garnacha"]
type: "Red wine"

count: 1
grape: ["Pinot Noir"]
type: "Red wine"

count: 1
grape: ["Pinot Noir"]
type: "Red wine" 

для каждой винной этикетки в моем наборе данных. Однако мне нужно, чтобы данные были такими:

count: 1
grape: "Pinot Noir"
type: "Red wine"

count: 1
grape: "Malbec"
type: "Red wine"

count: 1
grape: "Garnacha"
type: "Red wine"

count: 1
grape: "Pinot Noir"
type: "Red wine"

count: 1
grape: "Pinot Noir"
type: "Red wine"

Чтобы потом свести к этому:

count: 3
grape: "Pinot Noir"
type: "Red wine"

count: 1
grape: "Malbec"
type: "Red wine"

count: 1
grape: "Garnacha"
type: "Red wine"

Возможно ли это сделать через javascript?


person Eduarda Espindola    schedule 23.07.2019    source источник
comment
Вместо того, чтобы показывать, как вы получаете данные, просто скопируйте/вставьте свой CSV (не весь, а только некоторые строки). Поскольку вы, вероятно, все равно загружаете его с помощью d3.csv, мы можем найти лучший и самый быстрый способ получить структуру данных так, как вы хотите.   -  person Gerardo Furtado    schedule 23.07.2019


Ответы (3)


Вы можете попробовать следующий код.

var data = [
  { count: 1, grape: ["Pinot Noir", "Malbec", "Garnacha"], type: "Red wine" },
  { count: 1, grape: ["Pinot Noir"], type: "Red wine" },
  { count: 1, grape: ["Pinot Noir"], type: "Red wine" }
];

var newData = [];

data.forEach(item => {
  item.grape.forEach(g => {
    var addedItem = newData.find(f => f.type == item.type && f.grape == g);
    if (addedItem) {
      addedItem.count += +item.count;
    } else {
      newData.push({
        count: +item.count,
        grape: g,
        type: item.type
      });
    }
  });
});
console.log('Result: ', newData);

РЕДАКТИРОВАТЬ: код можно изменить, выполнив следующие действия для повышения производительности.

var data = [
  { count: 1, grape: ["Pinot Noir", "Malbec", "Garnacha"], type: "Red wine" },
  { count: 1, grape: ["Pinot Noir"], type: "Red wine" },
  { count: 1, grape: ["Pinot Noir"], type: "Red wine" }
];

var counts = {};
data.forEach(item => {
  item.grape.forEach(g => {
    if (counts[item.type]) {
      if (counts[item.type][g]) {
        counts[item.type][g] += +item.count;
      } else {
        counts[item.type][g] = +item.count;
      }
    } else {
      counts[item.type] = { [g]: item.count };
    }
  });
});

var newData = [];
Object.keys(counts).forEach(type => {
  Object.keys(counts[type]).forEach(grape => {
    newData.push({ count: counts[type][grape], grape: grape, type: type });
  });
});
console.log('Result: ', newData);

person Samuel    schedule 23.07.2019
comment
Если бы это было техническое собеседование, и вы изменили глобальную переменную с функцией, это было бы моментом, когда вы потеряли работу. - person Adrian Brand; 23.07.2019
comment
Спасибо @AdrianBrand. Но на самом деле она будет использоваться в функции, поэтому она не должна быть глобальной переменной. И в некоторых случаях было бы лучше использовать переменную вне loop, чтобы улучшить временную сложность. - person Samuel; 23.07.2019
comment
Если вам нужна наилучшая производительность, вы должны формировать свойства вашего объекта counts как объекты, которые будут отображаться в конечном массиве, а затем вы можете просто использовать Object.values(counts) для создания окончательного массива, повторяя массив для создания посредника объект только для итерации свойств и создания новых объектов для помещения в массив, безусловно, не является настройкой производительности. - person Adrian Brand; 23.07.2019

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

const data = [
  { count: 1, grape: ["Pinot Noir", "Malbec", "Garnacha"], type: "Red wine" },
  { count: 1, grape: ["Pinot Noir"], type: "Red wine" },
  { count: 1, grape: ["Pinot Noir"], type: "Red wine" }
];

const wineCount = data => data.reduce((results, item) => {
  item.grape.reduce((results, grape) => {
    const current = results.find(i => i.grape === grape);
    if (current) {
      current.count++;
    } else {
      results.push({ grape: grape, count: item.count, type: item.type });
    }
    return results;
  }, results);
  return results;
}, []);

console.log(wineCount(data));

person Adrian Brand    schedule 23.07.2019

Вашу проблему можно решить, разделив ее на 4 отдельных шага:

// Step 1: 
// [ [{grape, type}, {grape, type}, {grape, type}], [{grape, type}], [{grape, type}] ]
items = items.map(item => {
    return item.grape.map(grape => ({
        grape: grape,
        type: item.type
    }));
});

// Step 2: flatten the array
// [{grape, type}, {grape, type}, {grape, type}, {grape, type}, {grape, type}]
items = items.reduce((acc, arr) => ([...acc, ...arr]), []);

// Step 3: summarize
// {'Pinot Noir': {count, grape, type}, 'Malbec': {count, grape, type}, 'Garnacha': {count, grape, type}}
items = items.reduce((acc, item) => {
    const existingItem = acc[item.grape];
    return {
        ...acc,
        [item.grape]: {
            ...item,
            count: existingItem ? existingItem.count + 1 : 1
        }
    };
}, {});

// Step 4: Get object values
// [{count, grape, type}, {count, grape, type}, {count, grape, type}]
Object.keys(items).map(key => items[key]);

Шаг 1 + 2 вместе по сути представляют собой операцию flatMap:

Это даст вам промежуточный шаг, который вы определили.

Шаг 3 + 4 затем суммирует пункты.

person basilikum    schedule 23.07.2019