[英]Point-free group by multiple properties
我正在努力实现一个变量groupBy
,它允许以无点样式对多个属性进行分组。 (我正在使用打字稿和ramda)。
我想通过从函数getProperties :: A a -> [b]
返回的属性对类型A
的一些元素进行分组。 在命令式范例中,实现可能如下所示:
const getProperties = (item: Item): Array<keyof Item> => [item.x, item.y.z];
const groupByMany = (items: Item[]): Dictionary<Item[]> => {
let groupped: Dictionary<Item[]> = {};
for (let item of items) {
for (let key of getProperties(item)) {
if (groupped[key]) {
groupped[key].push(item);
} else {
groupped[key] = [item];
}
}
}
}
例:
const items = [
{ i: 1, x: 'A', y: { z: 'B' } },
{ i: 2, x: 'A' },
{ i: 3, x: 'B', y: { z: 'B' } },
];
const expectedOutput = {
A: [ { i: 1, ... }, { i: 2, ... }],
B: [ { i: 1, ... }, { i: 3, ... }],
};
我会帮你的 -
const reduce = (f, init, xs) => xs .reduce (f, init) const upsert = (m, k, v) => m .has (k) ? m .get (k) .push (v) : m .set (k, [ v ]) const groupByMany = (f, xs) => reduce ( (m, x) => ( f (x) .forEach (k => k && upsert (m, k, x)) , m ) , new Map , xs ) const items = [ { i: 1, x: 'A', y: { z: 'B' } } , { i: 2, x: 'A' } , { i: 3, x: 'B', y: { z: 'B' } } ] const result = groupByMany ( item => [ item.x, item.y && item.yz ] , items ) console.log(Object.fromEntries(result.entries()))
注意最后一项如何为.x
和 .yz
设置B
,以便将它插入B
组两次 。 我们更改upsert
以便它不会插入重复值 -
const upsert = (m, k, v) =>
m .has (k)
? m .get (k) .includes (v)
? m
: m .get (k) .push (v)
: m .set (k, [ v ])
展开下面的代码段,在自己的浏览器中查看最终结果 -
const reduce = (f, init, xs) => xs .reduce (f, init) const upsert = (m, k, v) => m .has (k) ? m .get (k) .includes (v) ? m : m .get (k) .push (v) : m .set (k, [ v ]) const groupByMany = (f, xs) => reduce ( (m, x) => ( f (x) .forEach (k => k && upsert (m, k, x)) , m ) , new Map , xs ) const items = [ { i: 1, x: 'A', y: { z: 'B' } } , { i: 2, x: 'A' } , { i: 3, x: 'B', y: { z: 'B' } } ] const result = groupByMany ( item => [ item.x, item.y && item.yz ] , items ) console.log(Object.fromEntries(result.entries()))
关于SO的特殊输出的注释:SO将不会显示相同的对象两次,而是它将为对象提供引用,并在复制对象出现的位置打印该引用。 例如/**id:3**/
在程序的输出中 -
{
"A": [
{
/**id:3**/
"i": 1,
"x": "A",
"y": {
"z": "B"
}
},
{
"i": 2,
"x": "A"
}
],
"B": [
/**ref:3**/,
{
"i": 3,
"x": "B",
"y": {
"z": "B"
}
}
]
}
哪个符合您的预期输出 -
const expectedOutput = {
A: [ { i: 1, ... }, { i: 2, ... }],
B: [ { i: 1, ... }, { i: 3, ... }],
};
这不像你要求的那样没有点,但我只是说我会让你开始......
我无法从问题中看出你是否想要一些能让你轻松编写无点代码的东西,或者由于某些原因你是否正在寻找一个实际的无点实现。 如果是后者,那么我担心这将无济于事。 但这很简单
const groupByMany = (fn) => (xs) => xs .reduce ( (a, x) => [...new Set ( fn(x) )] . reduce ( (a, k) => k ? {...a, [k]: [... (a [k] || []), x ] } : a , a ) , {} ) // const getProperties = (item) => [path(['x'], item), path(['y', 'z'], item)] const getProperties = juxt ( [path (['x']), path (['y', 'z']) ] ) const items = [{ i: 1, x: 'A', y: { z: 'B' } }, { i: 2, x: 'A'}, { i: 3, x: 'B', y: { z: 'B' } }] console .log ( groupByMany (getProperties) (items) )
<script src="https://bundle.run/ramda@0.26.1"></script></script> <script>const { juxt, path } = ramda </script>
通过[... new Set ( fn(x) ) ]
运行键只是消除fn (x)
返回的数组中重复项的快速方法。 功能的其余部分应该非常清楚。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.