Как сгруппировать массив вложенных объектов по трем различным свойствам в JS

У меня есть массив объектов, выглядящих так

[
 {store: {id: "1", metadata: {itemName: "Q", itemCode: "332", itemDueDate: "2021-10-28", …}}},
 {store: {id: "2", metadata: {itemName: "WA", itemCode: "190", itemDueDate: "2021-08-23", …}}},
 {store: {id: "3", metadata: {itemName: "P", itemCode: "199", itemDueDate: "2020-12-03", …}}}
]

Как я могу сгруппировать этот массив, используя три разных свойства из объекта метаданных, чтобы это было сочетание:

  1. название предмета
  2. код продукта
  3. itemDueDate

Я попытался использовать GroupWith Рамды, но я только что узнал, что они используют последовательный порядок при группировке.

export const groupedItems = (items: ArchiveItems) => R.groupWith<ArchiveItems>((a, b) => {
return a.store.metadata.itemName === b.store.metadata.itemName &&
       a.store.metadata.itemCode === b.store.metadata.itemCode &&
       a.store.metadata.itemDueDate === b.store.metadata.itemDueDate
})(items)

Всего 2 ответа


Да, группа groupWith предназначена для группировки последовательных элементов. groupBy - это то, что вы, вероятно, хотите. Требуется функция, которая преобразует ваш объект в строку, а затем группирует ваш массив в объект с массивом для каждой преобразованной строки. Вы можете создать эту строку так, как вам нравится, чаще всего путем извлечения одного свойства. Здесь мы можем объединить несколько свойств в строку:

const collect = pipe (
  groupBy (({store: {metadata: m}}) => `${m.itemName}~${m.itemCode}~${m.itemDueDate}`),
  values
)

const data = [{store: {id: "1", metadata: {itemName: "Q", itemCode: "332", itemDueDate: "2021-10-28", foo: "a"}}}, {store: {id: "2", metadata: {itemName: "WA", itemCode: "190", itemDueDate: "2021-08-23", foo: "b"}}}, {store: {id: "3", metadata: {itemName: "P", itemCode: "199", itemDueDate: "2020-12-03", foo: "c"}}}, {store: {id: "4", metadata: {itemName: "WA", itemCode: "190", itemDueDate: "2021-08-23", foo: "d"}}}, {store: {id: "5", metadata: {itemName: "P", itemCode: "199", itemDueDate: "2020-12-03", foo: "e"}}}, {store: {id: "6", metadata: {itemName: "WA", itemCode: "190", itemDueDate: "2021-08-23", foo: "f"}}}]

console .log (
  collect (data)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {pipe, groupBy, values} = R                           </script>

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

Обратите внимание, что вызов values в конце превращает это в массив массивов. Вы можете оставить исходный объект, но я думаю, что такие ключи, как "P~199~2020-12-03" вряд ли будут очень полезны. Или вам может потребоваться дальнейшая обработка для достижения вашей основной цели.

Если некоторые из ваших значений могут включать ~ , вам может понадобиться другой разделитель.


Можно попробовать уменьшить функцию

    const l: ArchiveItems[] = [
    {id: "1", metadata: {itemName: "Q", itemCode: "332", itemDueDate: "2021-10-28"}},
    {id: "2", metadata: {itemName: "WA", itemCode: "190", itemDueDate: "2021-08-23"}},
    {id: "3", metadata: {itemName: "P", itemCode: "199", itemDueDate: "2020-12-03"}}
];

const result = l.reduce<Map<string, ArchiveItems[]>>((acc, cv) => {
    const cvKey = `${cv.metadata.itemName}_${cv.metadata.itemCode}_${cv.metadata.itemDueDate}`;
    if (acc.get(cvKey) !== undefined) {
        acc.set(cvKey, [...acc.get(cvKey), cv])
    } else {
        acc.set(cvKey, [cv])
    }
    return acc;
}, new Map());

Есть идеи?

10000