Частичные линзы: группируйте массив объектов по свойству, используйте значение проп в качестве ключа - PullRequest
0 голосов
/ 15 ноября 2018

У меня есть массив таких объектов:

[
  { name: "Group 1", value: "Foo" },
  { name: "Group 2", value: "Bar" },
  { name: "Group 1", value: "Baz" }
]

Я бы хотел использовать библиотеку Частичные линзы , чтобы преобразовать эти группы в ключи объекта с элементами соответствующей группы,вот так:

{
  "Group 1": [
    { name: "Group 1", value: "Foo" },
    { name: "Group 1", value: "Baz" }
  ],
  "Group 2": [
    { name: "Group 2", value: "Bar" }
  ]
}

Мой нынешний подход такой: предполагается, что у меня есть исходные данные в переменной с именем data:

const grouped = L.collect([L.groupBy('name'), L.entries], data)
const setKey = [L.elems, 0]
const getName = [L.elems, 1, 0, 'name']
const correctPairs = L.disperse(setKey, L.collectTotal(getName, grouped), grouped)
L.get(L.inverse(L.keyed), correctPairs)

Мне не нравится, что мне нужноиспользовать переменные grouped и correctPairs для хранения данных, поскольку я, вероятно, должен быть в состоянии выполнить преобразование непосредственно в композиции.Не могли бы вы помочь мне составить ту же функциональность более осмысленным образом?

Вот игровая площадка с частичными линзами 1018 * с приведенным выше кодом.

Ответы [ 3 ]

0 голосов
/ 15 ноября 2018

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

const groupBy = key => (result,current) => {
  let item = Object.assign({},current);
  // optional
  // delete item[key];
  if (typeof result[current[key]] == 'undefined'){
    result[current[key]] = [item];
  }else{
    result[current[key]].push(item);
  }
  return result;
};

const data = [{ name: "Group 1", value: "Foo" },{ name: "Group 2", value: "Bar" },{ name: "Group 1", value: "Baz" }];

const grouped = data.reduce(groupBy('name'),{});
console.log(grouped);
const groupedByValue = data.reduce(groupBy('value'),{});
console.log(groupedByValue);
0 голосов
/ 15 ноября 2018

Я предполагаю, что цель состоит в том, чтобы на самом деле создать изоморфизм, с помощью которого можно рассматривать такой массив как объект массивов, а также выполнять обновления.Как и в случае двунаправленной версии, например, функции R.groupBy Рамды.

Действительно, одним из подходов было бы просто использовать R.groupBy Рамды для реализации нового примитивного изоморфизма с использованиемL.iso.Примерно так:

const objectBy = keyL => L.iso(
  R.cond([[R.is(Array), R.groupBy(L.get(keyL))]]),
  R.cond([[R.is(Object), L.collect([L.values, L.elems])]])
)

Условные обозначения необходимы, чтобы учесть вероятность того, что данные не относятся к ожидаемому типу, и отобразить результат в undefined, если это не так.

Вот игровая площадка с вышеуказанной реализацией objectBy, основанной на Рамде.

Используя только текущую версию частичных линз, одним из способов создания аналогичного комбинатора objectBy будетследующим образом:

const objectBy = keyL => [
  L.groupBy(keyL),
  L.array(L.unzipWith1(L.iso(x => [L.get(keyL, x), x], L.get(1)))),
  L.inverse(L.keyed)
]

Возможно, интересной частью выше является средняя часть, которая преобразует массив массивов в массив пар ключ-массив (или наоборот).L.unzipWith1 проверяет, что все ключи в группе совпадают, и если они этого не делают, эта группа будет сопоставлена ​​с undefined и отфильтрована по L.array.При желании можно добиться более строгого поведения, используя L.arrays.

Вот игровая площадка с вышеописанной objectBy реализацией.

0 голосов
/ 15 ноября 2018

Вы можете использовать Array.reduce

let arr = [{ name: "Group 1", value: "Foo" },{ name: "Group 2", value: "Bar" },{ name: "Group 1", value: "Baz" }];

let obj = arr.reduce((a,c) => Object.assign(a, {[c.name]: (a[c.name] || []).concat(c)}), {});
console.log(obj);
...