具有属性功能的多级分组依据

时间:2018-12-17 22:43:33

标签: javascript ramda.js

这与multi level groupby ramda js相似,但有一种扭曲,给我带来麻烦。除了两级分组外,我还希望内部分组依据是经过处理的属性值版本。

考虑这样的数据:

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"
            }
        ]
    }
}

我感觉有点傻,我无法得到这个。有任何想法吗? Here是一个玩耍的地方。

3 个答案:

答案 0 :(得分:1)

您非常亲密。只需将这些功能与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 = pipe(
  groupBy(prop("top")),
  map(groupBy(pipe(prop("name"), 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(data))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

此功能当然可以简化很多,内联起来可能更容易:

const groupByTopThenZone = pipe(
  groupBy(prop("top")),
  map(groupBy(pipe(prop("name"), slice(5, -4)))
)

要记住的主要事情是groupBy不一定与prop捆绑在一起。我们可以对 any String / Number / Symbol生成函数的结果进行分组。

答案 1 :(得分:0)

这不是使用ramda,而是使用香草JS。

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 res = data.reduce((acc, val, ind, arr) => {
  const top = val.top;
  // if the top does not exist in the obj, create it
  if (!acc[top]) {
    acc[top] = {};
  }
  // get the key through split. you could also use a regex here
  const keyFragments = val.name.split('-');
  const key = [keyFragments[1], keyFragments[2]].join('-');
  // if the key obj prop does not exist yet, create the array
  if (!acc[top][key]) {
    acc[top][key] = []; 
  }
  // push the value
  acc[top][key].push({ name: val.name, top: val.top });
  return acc;
}, {});
console.log(res);

答案 2 :(得分:0)

另一种方法是构造每个最终对象并将其全部合并:

您可以变换此对象:

{
  "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;

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 key = slice(5, -4);

const obj = ({top, name}) => ({
  [top]: {
    [key(name)]: [
      {top, name}
    ]
  }
});

const groupByTopTenZone = reduce(useWith(mergeDeepWith(concat), [identity, obj]), {});

console.log(
  
  groupByTopTenZone(data)

)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>