由多个 groupBy 函数嵌套的对象数组
Group array of objects nested by several groupBy functions
我尝试使用 Ramda that applies several groupBy 函数 groupBys
将函数 groupByMult
写入对象数组 input
:
function groupByMult(groupBys, input) { ... }
它应该 return 具有子属性 groupBys[0]
、孙属性 groupBys[1]
、孙属性 groupBys[2]
等的嵌套对象。最后一个孙子 属性 具有属于该组路径的对象数组的值(请参阅底部的 expected
输出)。
我用一个例子来解释期望的行为:
我的输入是一个对象数组。在此示例中,所有对象都有 属性 g1
、g2
和 g3
.
const input = [
{ g1: 'g1a', g2: 'g2a', g3: true },
{ g1: 'g1b', g2: 'g2b', g3: 42 },
{ g1: 'g1a', g2: 'g2a', g3: 'text' },
{ g1: 'g1a', g2: 'g2a', g3: false },
{ g1: 'g1a', g2: 'g2b', g3: 0 },
{ g1: 'g1a', g2: 'g2b', g3: 1 },
{ g1: 'g1a', g2: 'g2b', g3: true },
];
在我的示例中,我的组函数是:
const groupBys = [
R.prop('g1'),
R.prop('g2'),
R.compose(R.type, R.prop('g3'))
];
我这样叫groupByMult
const outpout = groupByMult(groupBys, input);
我希望 output
与 expected
:
深度相等
const expected = {
g1a: {
g2a: {
Boolean: [
{ g1: 'g1a', g2: 'g2a', g3: true },
{ g1: 'g1a', g2: 'g2a', g3: false },
],
String: [
{ g1: 'g1a', g2: 'g2a', g3: 'text' },
],
},
g2b: {
Number: [
{ g1: 'g1a', g2: 'g2b', g3: 0 },
{ g1: 'g1a', g2: 'g2b', g3: 1 },
],
Boolean: [
{ g1: 'g1a', g2: 'g2b', g3: true },
],
},
},
g1b: {
g2b: {
Number: [
{ g1: 'g1b', g2: 'g2b', g3: 42 },
]
},
},
}
output
或 expected
具有子属性 g1a
、g1b
等 groupBys[0]
、孙属性 g2a
、g2b
、groupBys[1]
的孙属性 Boolean
、Number
或 groupBys[2]
的 String
等,它们具有属于的对象数组这个组路径。例如,数组 output.g1a.g2b.Boolean
的所有对象看起来都像 { g1: 'g1a', g2: 'g2b', Boolean: <boolean> }
,其中 <boolean>
表示任何布尔值。
如何实施 groupByMult
以获得所描述的行为?
减少函数数组并累积映射组合。对于嵌套的分组对象,我们使用 map(map(fn), input) 再次对值进行分组:
const groupByMult = R.curry(function(fns, input) {
const groupByMultReduced = R.reduce(function(acc, fn) {
if (acc === null) {
return {
fn: R.groupBy(fn),
map: R.map
};
}
return {
// main function
fn: R.compose(acc.map(R.groupBy(fn)), acc.fn),
// accumulating helper map(map(map(...
map: R.compose(R.map, acc.map)
};
}, null, fns);
return groupByMultReduced !== null ? groupByMultReduced.fn(input) : input;
});
let output = groupByMult(groupBys, input);
这应该可以通过一个函数来实现,该函数在应用 R.groupBy
后递归映射结果对象值。
groupByMany = R.curry((fns, items) =>
R.isEmpty(fns) ? items
: R.map(groupByMany(R.tail(fns)),
R.groupBy(R.head(fns), items)));
此函数将获取分组函数列表和对象列表,并继续递归调用自身,直到没有更多函数可作为分组依据。
例如:
input = [
{ g1: 'g1a', g2: 'g2a', g3: 'g3a' },
{ g1: 'g1b', g2: 'g2c', g3: 'g3d' },
{ g1: 'g1c', g2: 'g2a', g3: 'g3b' },
{ g1: 'g1a', g2: 'g2b', g3: 'g3a' },
{ g1: 'g1a', g2: 'g2b', g3: 'g3b' }
];
groupByMany([R.prop('g1'), R.prop('g2'), R.prop('g3')], input);
结果类似于:
{ "g1a": { "g2a": { "g3a": [{ "g1": "g1a", "g2": "g2a", "g3": "g3a" }] },
"g2b": { "g3a": [{ "g1": "g1a", "g2": "g2b", "g3": "g3a" }],
"g3b": [{ "g1": "g1a", "g2": "g2b", "g3": "g3b" }] } },
"g1b": { "g2c": { "g3d": [{ "g1": "g1b", "g2": "g2c", "g3": "g3d" }] } },
"g1c": { "g2a": { "g3b": [{ "g1": "g1c", "g2": "g2a", "g3": "g3b" }] } } }
在我看来你想做的事情,你的例子是这样的:
const groups = R.pipe(
R.groupBy(R.prop('g1')),
R.map(R.groupBy(R.prop('g2'))),
R.map(R.map(R.groupBy(R.compose(R.type, R.prop('g3')))))
);
基于这样的输入:
[
R.prop('g1'),
R.prop('g2'),
R.compose(R.type, R.prop('g3'))
]
用 groupBy
包装列表中的每个函数并不困难。那只是一个 map
调用。但是,为每个元素制作不断增加的 map
列表需要做一些工作。我不确定是否有比这个递归 addMap
助手更优雅或更高效的东西,但这是我想出的:
const groupByMults = (() => {
const addMap = fns => fns.length < 1 ? [] :
[R.head(fns)].concat(addMap(R.map(R.map, R.tail(fns))));
return R.curry((groupers, obj) => {
var fns = addMap(R.map(R.groupBy, groupers))
return R.apply(R.pipe)(fns)(obj);
});
}());
您可以在 the Ramda REPL
上看到这个效果
我尝试使用 Ramda that applies several groupBy 函数 groupBys
将函数 groupByMult
写入对象数组 input
:
function groupByMult(groupBys, input) { ... }
它应该 return 具有子属性 groupBys[0]
、孙属性 groupBys[1]
、孙属性 groupBys[2]
等的嵌套对象。最后一个孙子 属性 具有属于该组路径的对象数组的值(请参阅底部的 expected
输出)。
我用一个例子来解释期望的行为:
我的输入是一个对象数组。在此示例中,所有对象都有 属性 g1
、g2
和 g3
.
const input = [
{ g1: 'g1a', g2: 'g2a', g3: true },
{ g1: 'g1b', g2: 'g2b', g3: 42 },
{ g1: 'g1a', g2: 'g2a', g3: 'text' },
{ g1: 'g1a', g2: 'g2a', g3: false },
{ g1: 'g1a', g2: 'g2b', g3: 0 },
{ g1: 'g1a', g2: 'g2b', g3: 1 },
{ g1: 'g1a', g2: 'g2b', g3: true },
];
在我的示例中,我的组函数是:
const groupBys = [
R.prop('g1'),
R.prop('g2'),
R.compose(R.type, R.prop('g3'))
];
我这样叫groupByMult
const outpout = groupByMult(groupBys, input);
我希望 output
与 expected
:
const expected = {
g1a: {
g2a: {
Boolean: [
{ g1: 'g1a', g2: 'g2a', g3: true },
{ g1: 'g1a', g2: 'g2a', g3: false },
],
String: [
{ g1: 'g1a', g2: 'g2a', g3: 'text' },
],
},
g2b: {
Number: [
{ g1: 'g1a', g2: 'g2b', g3: 0 },
{ g1: 'g1a', g2: 'g2b', g3: 1 },
],
Boolean: [
{ g1: 'g1a', g2: 'g2b', g3: true },
],
},
},
g1b: {
g2b: {
Number: [
{ g1: 'g1b', g2: 'g2b', g3: 42 },
]
},
},
}
output
或 expected
具有子属性 g1a
、g1b
等 groupBys[0]
、孙属性 g2a
、g2b
、groupBys[1]
的孙属性 Boolean
、Number
或 groupBys[2]
的 String
等,它们具有属于的对象数组这个组路径。例如,数组 output.g1a.g2b.Boolean
的所有对象看起来都像 { g1: 'g1a', g2: 'g2b', Boolean: <boolean> }
,其中 <boolean>
表示任何布尔值。
如何实施 groupByMult
以获得所描述的行为?
减少函数数组并累积映射组合。对于嵌套的分组对象,我们使用 map(map(fn), input) 再次对值进行分组:
const groupByMult = R.curry(function(fns, input) {
const groupByMultReduced = R.reduce(function(acc, fn) {
if (acc === null) {
return {
fn: R.groupBy(fn),
map: R.map
};
}
return {
// main function
fn: R.compose(acc.map(R.groupBy(fn)), acc.fn),
// accumulating helper map(map(map(...
map: R.compose(R.map, acc.map)
};
}, null, fns);
return groupByMultReduced !== null ? groupByMultReduced.fn(input) : input;
});
let output = groupByMult(groupBys, input);
这应该可以通过一个函数来实现,该函数在应用 R.groupBy
后递归映射结果对象值。
groupByMany = R.curry((fns, items) =>
R.isEmpty(fns) ? items
: R.map(groupByMany(R.tail(fns)),
R.groupBy(R.head(fns), items)));
此函数将获取分组函数列表和对象列表,并继续递归调用自身,直到没有更多函数可作为分组依据。
例如:
input = [
{ g1: 'g1a', g2: 'g2a', g3: 'g3a' },
{ g1: 'g1b', g2: 'g2c', g3: 'g3d' },
{ g1: 'g1c', g2: 'g2a', g3: 'g3b' },
{ g1: 'g1a', g2: 'g2b', g3: 'g3a' },
{ g1: 'g1a', g2: 'g2b', g3: 'g3b' }
];
groupByMany([R.prop('g1'), R.prop('g2'), R.prop('g3')], input);
结果类似于:
{ "g1a": { "g2a": { "g3a": [{ "g1": "g1a", "g2": "g2a", "g3": "g3a" }] },
"g2b": { "g3a": [{ "g1": "g1a", "g2": "g2b", "g3": "g3a" }],
"g3b": [{ "g1": "g1a", "g2": "g2b", "g3": "g3b" }] } },
"g1b": { "g2c": { "g3d": [{ "g1": "g1b", "g2": "g2c", "g3": "g3d" }] } },
"g1c": { "g2a": { "g3b": [{ "g1": "g1c", "g2": "g2a", "g3": "g3b" }] } } }
在我看来你想做的事情,你的例子是这样的:
const groups = R.pipe(
R.groupBy(R.prop('g1')),
R.map(R.groupBy(R.prop('g2'))),
R.map(R.map(R.groupBy(R.compose(R.type, R.prop('g3')))))
);
基于这样的输入:
[
R.prop('g1'),
R.prop('g2'),
R.compose(R.type, R.prop('g3'))
]
用 groupBy
包装列表中的每个函数并不困难。那只是一个 map
调用。但是,为每个元素制作不断增加的 map
列表需要做一些工作。我不确定是否有比这个递归 addMap
助手更优雅或更高效的东西,但这是我想出的:
const groupByMults = (() => {
const addMap = fns => fns.length < 1 ? [] :
[R.head(fns)].concat(addMap(R.map(R.map, R.tail(fns))));
return R.curry((groupers, obj) => {
var fns = addMap(R.map(R.groupBy, groupers))
return R.apply(R.pipe)(fns)(obj);
});
}());
您可以在 the Ramda REPL
上看到这个效果