我在实现变体groupBy
方面有些费力,该变体允许以无点样式对多个属性进行分组。 (我正在使用打字稿和ramda)。
我想通过功能A
返回的属性来归类为getProperties :: A a -> [b]
类型的某些元素。在命令式范例中,实现可能如下所示:
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, ... }],
};
答案 0 :(得分: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.y.z ]
, items
)
console.log(Object.fromEntries(result.entries()))
请注意,最后一项对B
和 .x
具有.y.z
的情况,因此将其插入到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.y.z ]
, 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, ... }],
};
这并非像您所要求的那样毫无意义,但我只是说我会让您入门...
答案 1 :(得分:0)
我无法从问题中得知您是否想要使您易于进行无点编码的东西,或者是否出于某种原因正在寻找一种实际的无点实现。如果是后者,那么恐怕这将无济于事。但这很简单
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)
返回的数组中消除重复项的快速方法。该功能的其余部分应该很清楚。