Javascript 属性上具有函数的多级groupby

Javascript 属性上具有函数的多级groupby,javascript,ramda.js,Javascript,Ramda.js,这类似于,但有一个扭曲,这给我带来了麻烦。除了两级分组之外,我希望内部GROUPBY位于属性值的处理版本上 考虑如下数据: const data = [ { top: 'top1', name: 'junk-key-a-101' }, { top: 'top1', name: 'junk-key-b-102' }, { top: 'top2', name: 'junk-key-c-103' }, { top: 'top2', name: 'jun

这类似于,但有一个扭曲,这给我带来了麻烦。除了两级分组之外,我希望内部GROUPBY位于属性值的处理版本上

考虑如下数据:

const data = [ 
  { top: 'top1',
    name: 'junk-key-a-101' },
  { top: 'top1',
    name: 'junk-key-b-102' },
  { top: 'top2',
    name: 'junk-key-c-103' },
  { top: 'top2',
    name: 'junk-key-c-104' } ];
我可以拔出钥匙,对其进行处理,使其独一无二,如下所示:

const getZoneFromName = n =>  join('-', slice(1, 3, split('-', n)));
uniq(map(getZoneFromName, pluck('name', data)));
这将给我一个很好的列表:

["key-a", "key-b", "key-c"]
我可以将列表分为两个级别:

const groupByTopThenZone = pipe(
  groupBy(prop("top")),
  map(groupBy(prop("name")))
);
groupByTopThenZone(data);
但我不知道如何组合它们以获得以下输出:

{
    top1: {
        "key-a": [
            {
                name: "junk-key-a-101",
                top: "top1"
            }
        ],
        "key-b": [
            {
                name: "junk-key-b-102",
                top: "top1"
            }
        ]
    },
    top2: {
        "key-c": [
            {
                name: "junk-key-c-103",
                top: "top2"
            },
            {
                name: "junk-key-c-104",
                top: "top2"
            }
        ]
    }
}

我觉得有点傻,我不能得到这个。有什么想法吗?这不是使用ramda,而是使用vanilla JS

const data=[
{top:'top1',
名称:'junk-key-a-101'},
{top:'top1',
名称:'junk-key-b-102'},
{top:'top2',
名称:'junk-key-c-103'},
{top:'top2',
名称:'junk-key-c-104'}];
const res=数据减少((acc、val、ind、arr)=>{
const top=val.top;
//如果obj中不存在顶部,请创建它
如果(!acc[顶部]){
acc[top]={};
}
//通过拆分获取密钥。您也可以在此处使用正则表达式
const keyFragments=val.name.split('-');
常量键=[keyFragments[1],keyFragments[2]].join('-');
//如果关键obj道具尚不存在,请创建阵列
如果(!acc[顶部][键]){
acc[顶部][键]=[];
}
//推动价值
acc[top][key].push({name:val.name,top:val.top});
返回acc;
}, {});

控制台日志(res)你们非常接近。只要将这些函数与
compose
/
pipe
组合就可以了

(注意这里还有一个简化版的
getZoneFromName

const{pipe,groupBy,map,prop,slice}=R
//const getZoneFromName=n=>join('-',slice(1,3,split('-',n));
const getZoneFromName=slice(5,-4)
const groupByTopThenZone=管道(
groupBy(道具(“top”),
地图(groupBy(管道(道具(“名称”)、getZoneFromName)))
)
const data=[{“name”:“junk-key-a-101”,“top”:“top1”},{“name”:“junk-key-b-102”,“top”:“top1”},{“name”:“junk-key-c-103”,“top”:“top2”},{“name”:“junk-key-c-104”,“top”:“top2”}]
console.log(groupByTopThenZone(数据))

另一种方法是构造每个最终对象并合并它们:

可以变换此对象:

{
  "top": "top1",
  "name": "junk-key-a-101"
}
在这一点上:

{
  "top1": {
    "key-a": [
      {
        "name": "junk-key-a-101",
        "top": "top1"
      }
    ]
  }
}
具有以下功能:

const key = slice(5, -4);

const obj = ({top, name}) => ({
  [top]: {
    [key(name)]: [
      {top, name}
    ]
  }
});
现在,您可以对数据进行迭代,变换每个对象并将它们合并在一起:

const groupByTopTenZone = reduce(useWith(mergeDeepWith(concat), [identity, obj]), {});
完整示例:

const{slice,useWith,identity,reduce,mergeDeepWith,concat}=R;
常量数据=[
{top:'top1',
名称:'junk-key-a-101'},
{top:'top1',
名称:'junk-key-b-102'},
{top:'top2',
名称:'junk-key-c-103'},
{top:'top2',
名称:'junk-key-c-104'}
];
常数键=切片(5,-4);
常量obj=({top,name})=>({
[上图]:{
[钥匙(姓名)]:[
{top,name}
]
}
});
const groupByTopTenZone=reduce(useWith(mergeDeepWith(concat),[identity,obj]),{});
console.log(
groupByTopTenZone(数据)
)

谢谢,@quirimo,感谢你花时间回答,这与我的非ramda解决方案非常相似(我想说的是非功能性的,但这不会是正确的)。没问题:)有时候我确实更喜欢香草js而不是这些框架,因为即使它们让你的工作非常简单,有时候你会在对象/数组上循环几次,也许你只需要一次迭代就可以完成任务谢谢Scott。这是一个很大的帮助。在实际数据中,为一个键使用名称是有点复杂的,分割片连接是一个更可靠的解决方案,但是,是的,寻求简化会有所帮助。我确实对管道的两种用途有些困惑。我觉得他们的工作方式和我完全相反。内部的一个感觉像道具(“名字”)进入片饲料。外部的“感觉”就像地图输入groupBy(道具(“顶部”))。我在这一点上显然是错误的,但这会阻止我自己提出类似的解决方案。你能让我走上正确的轨道吗?@KevinO'Connor:它们都按照你的直觉所说的方式工作。外部的一个首先按
top
分组,产生
{top1:[…],top2:[…]}
。然后对该结果调用
map
。注意,
map(square,{a:1,b:2,c:3})/=>{a:1,b:4,c:9}
。这与使用
map
提供的函数转换对象中存储在
top1
top2
中的数组的方式相同,这里是内部
groupBy(…)
。如果你真的想要另一个方向的东西,那就是它的镜像孪生兄弟。是的,我明白了。再次感谢,@ScottSauyet!我希望这有助于我以后更好地思考这个问题。我喜欢直接使用函数来创建对象。我曾经尝试过类似的方法(没有您添加的键方法),然后是evolve,但是在为后续的类似键的方法处理正确的项时增加了麻烦。谢谢你的另一种方法。