如何通过 JS 中的三个不同属性对嵌套对象数组进行分组
How to group an array of nested objects by three different properties in 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", …}}}
]
如何使用元数据对象中的三个不同属性对这个数组进行分组,因此它将是以下内容的组合:
- 物品名称
- 物品代码
- itemDueDate
我尝试使用 Ramda 的 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)
可能会尝试 reduce 函数
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());
是的,Ramda 的 groupWith
is designed for grouping consecutive elements. 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"
这样的键可能不会很有帮助。或者您可能必须进行进一步处理才能实现您的基本目标。
如果您的某些值可能包含 ~
,您可能需要不同的分隔符。
我有一组看起来像这样的对象
[
{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", …}}}
]
如何使用元数据对象中的三个不同属性对这个数组进行分组,因此它将是以下内容的组合:
- 物品名称
- 物品代码
- itemDueDate
我尝试使用 Ramda 的 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)
可能会尝试 reduce 函数
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());
是的,Ramda 的 groupWith
is designed for grouping consecutive elements. 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"
这样的键可能不会很有帮助。或者您可能必须进行进一步处理才能实现您的基本目标。
如果您的某些值可能包含 ~
,您可能需要不同的分隔符。