[英]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.