我有一个场景,我有一个像这样的对象。
[
{attributeGroupId:2, attributeId: 11, name: 'Diamond'}
{attributeGroupId:1, attributeId: 9, name: '916'}
{attributeGroupId:1, attributeId: 1, name: '24K'}
{attributeGroupId:2, attributeId: 12, name: 'Square'}
]
预期结果:
[
{attributeGroupId:2, attributeId: 11, name: 'Diamond'},
{attributeGroupId:2, attributeId: 12, name: 'Square'}
]
,
[
{attributeGroupId:1, attributeId: 9, name: '916'},
{attributeGroupId:1, attributeId: 1, name: '24K'}
]
所以我可以像这样制作笛卡儿的乘积,
[
{attributeId: 11-9, name: 'Diamond-916'},
{attributeId: 11-1, name: 'Diamond-24K'},
{attributeId: 12-9, name: 'Square-916'},
{attributeId: 12-1, name: 'Square-24K'},
]
现在,我希望保持这个逻辑尽可能通用,因为运行时不知道attributeGroupId的数量。
我认为根据attributeGroupId将数组拆分为多个较小的数组应该是第一步。
发布于 2022-09-24 06:13:22
您可以实现这样的基本过滤器函数:
function filterForAttributeGroupId(data, id) {
return data.filter((item) => item.attributeGroupId === id);
}
console.log(filterForAttributeGroupId(data, 1)) //contains all elements with attributeGroupId = 1
console.log(filterForAttributeGroupId(data, 2)) //contains all elements with attributeGroupId = 2
这里有一个通用解决方案,返回一个过滤数组数组:
function filterForAttributeGroupId(data) {
const mem = {};
data.forEach((item) => {
if ( mem[item.attributeGroupId] ) {
mem[item.attributeGroupId].push(item);
} else {
mem[item.attributeGroupId] = [item];
}
})
return Object.values(mem);
}
编辑在注释反馈后,如果连接属性的顺序无关紧要,可以使用以下代码获得n个不同数组的“笛卡儿级联”:
function cartesianProduct(arrays) {
if (arrays.length <= 1 ) return arrays[0];
const first = arrays[0];
const second = cartesianProduct(arrays.slice(1));
const result = [];
first.forEach(( itemFirst ) => {
second.forEach( (itemSecond) => {
result.push({attributeId: `${itemFirst.attributeId}-${itemSecond.attributeId}`, name: `${itemFirst.name}-${itemSecond.name}`})
});
});
return result;
}
通过这种方式调用以下内容:
console.log(cartesianProduct(filterForAttributeGroupId(data)));
在预期数据中的结果(尽管字符串按另一个顺序连接):
[
{
"attributeId":"9-11",
"name":"916-Diamond"
},
{
"attributeId":"9-12",
"name":"916-Square"
},
{
"attributeId":"1-11",
"name":"24K-Diamond"
},
{
"attributeId":"1-12",
"name":"24K-Square"
}
]
发布于 2022-09-26 16:31:45
我认为最好把这个逻辑分解成几块。首先,数组的笛卡儿乘积函数似乎非常有用。因此,让我们为它建立一个实用函数。其次,基于某些共享特性将数组分解为子数组也很常见。所以让我们再做一个。
然后,我们可以编写一个简单的主函数,该函数按attributeGroupId
对id进行分组,并对其调用笛卡尔积,然后对结果数组中的每个项调用如下
[
{attributeGroupId: 2, attributeId: 11, name: "Diamond"},
{attributeGroupId: 1, attributeId: 9, name: "916"}
]
我们可以通过组合attributeId
和name
属性来创建一个新对象。
const cartesian = ([xs, ...xss]) =>
xs == undefined ? [[]] : xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const group = (fn, k) => (xs) => Object .values (xs .reduce (
(a, x) => ((k = 'x' + fn (x)), (a [k] = a [k] || []), (a [k] .push (x)), a), {}
))
const regroupAtts = (xs) =>
cartesian (group (x => x .attributeGroupId) (xs))
.map (xs => ({
attributeId: xs .map (x => x .attributeId) .join ('-'),
name: xs .map (x => x.name) .join ('-')
}))
const atts = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}]
const atts2 = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 3, attributeId: 101, name: 'foo'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 3, attributeId: 102, name: 'bar'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}, {attributeGroupId: 3, attributeId: 103, name: 'baz'}]
console .log ('Original:', regroupAtts (atts))
console .log ('With third group added:', regroupAtts (atts2))
.as-console-wrapper {max-height: 100% !important; top: 0}
我们添加了第三个属性,包括名称"foo"
、"bar"
和"baz"
,以说明如何将其扩展到更多的属性。当然,如果添加太多这样的属性,您将不得不考虑组合爆炸,但是代码是为了处理它而设计的。
这里有趣的抽象是,如果您想参数化名称"attributeGroupId"
、"attributeId"
和"name"
。我们可以选择这样做:
const regroup = (key, props) => (xs) =>
cartesian (group (x => x [key]) (xs))
.map (xs => Object .fromEntries (
props .map (prop => [prop, xs .map (x => x [prop]) .join ('-')])
))
const regroupAtts = regroup ('attributeGroupId', ['attributeId', 'name'])
const cartesian = ([xs, ...xss]) =>
xs == undefined ? [[]] : xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))
const group = (fn, k) => (xs) => Object .values (xs .reduce (
(a, x) => ((k = 'x' + fn (x)), (a [k] = a[k] || []), (a[k] .push (x)), a), {}
))
const regroup = (key, props) => (xs) =>
cartesian (group (x => x [key]) (xs))
.map (xs => Object .fromEntries (
props .map (prop => [prop, xs .map (x => x [prop]) .join ('-')])
))
const regroupAtts = regroup ('attributeGroupId', ['attributeId', 'name'])
const atts = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}]
const atts2 = [{attributeGroupId: 2, attributeId: 11, name: 'Diamond'}, {attributeGroupId: 1, attributeId: 9, name: '916'}, {attributeGroupId: 3, attributeId: 101, name: 'foo'}, {attributeGroupId: 1, attributeId: 1, name: '24K'}, {attributeGroupId: 3, attributeId: 102, name: 'bar'}, {attributeGroupId: 2, attributeId: 12, name: 'Square'}, {attributeGroupId: 3, attributeId: 103, name: 'baz'}]
console .log ('Original:', regroupAtts (atts))
console .log ('With third group added:', regroupAtts (atts2))
.as-console-wrapper {max-height: 100% !important; top: 0}
以这种方式编写它,我们现在有了可重用的cartesian
和group
函数,这些函数经常出现。事实上,我从我以前写的答案里偷了这些。(我确实通过将group
添加到密钥生成中来稍微改变了'x' +
。这是因为您的密钥(attributeGroupId
s)是简单的数字。当Object.values
像这样迭代一个对象时,它首先按数字顺序执行这些操作,然后按插入顺序迭代剩余的字符串键,然后按符号进行迭代。通过在字符串前面加上一个字符串,我将所有这些都按插入顺序排列,并返回您所喜欢的顺序。我试图弄清楚这个函数的这个变体是否取代了我通常工具箱中的那个。)
https://stackoverflow.com/questions/73834942
复制相似问题