使用 Ramda 或类似工具从非结构化数据数组创建组

Create groups from an unstructured data array using Ramda or similar

给定一个包含这种形式的对象的数组:

[{data: {set: 'set1', option: 'option1'}},
 {data: {set: 'set1', option: 'option2'}},
 {data: {set: 'set1', option: 'option3'}},
 {data: {set: 'set2', option: 'optionA'}},
 {data: {set: 'set2', option: 'optionB'}}]

如何获得如下所示的数组:

[{set: 'set1', options: ['option1', 'option2', 'option3']},
 {set: 'set2', options: ['optionA', 'optionB']}]

我想使用函数式编程、Ramda 或原生 JS 方法。谢谢

我喜欢通过几个步骤来思考这样的问题,这些步骤使我不断朝着我想要的结果前进。有时这意味着我错过了一个更优雅的解决方案,但它通常可以更容易地想出一些有效的方法。

让我们使用 Ramda 以这种方式查看您的问题。


第 1 步:删除不必要的数据

你不需要那个外层 data 属性。由于您的数据是一个对象列表,并且每个对象都有一个 data 属性 来保存我们想要的数据,我们可以简单地对每个对象调用 prop('data')

要对每个调用它,我们可以使用 map,给我们 map(prop('data'))

因为这是一个很常见的用法,Ramda 还提供了一个以这种方式组合 mapprop 的函数:pluck('data')。将其应用于您的输入,我们得到:

[
  {option: "option1", set: "set1"},
  {option: "option2", set: "set1"},
  {option: "option3", set: "set1"},
  {option: "optionA", set: "set2"}
  {option: "optionB", set: "set2"}
]

这是一个好的开始。现在我们需要考虑将它们组合成类似的组。

第 2 步:分组数据

我们希望将共享 set 属性 的所有元素组合在一起。 Ramda 提供 groupBy,它接受一个将项目变成分组键的函数。我们想按 set 属性 分组,所以我们可以再次使用 prop,并根据之前的结果调用 groupBy(prop('set'))

这产生:

{
  set1: [
    {option: "option1", set: "set1"},
    {option: "option2", set: "set1"},
    {option: "option3", set: "set1"},
  ],
  set2: [
    {option: "optionA", set: "set2"}
    {option: "optionB", set: "set2"}
  ]
}

里面有冗余信息。我们需要在某个地方解决这个问题。但我会在尝试将其他部分组合在一起时稍微保存一下。

第 3 步:组合选项

我们已经看过 pluck。看起来我们可以在 set1set2 上使用它。好吧 map 也适用于对象,所以如果我们简单地调用 map(pluck('option')) 最后一个我们得到这个:

{
  set1: ["option1", "option2", "option3"], 
  set2: ["optionA", "optionB"]
}

哦,看,那也摆脱了冗余。这看起来非常接近所需的输出。

第 4 步:重建对象

但是现在我没有看到内置的 Ramda 函数可以让我一路走下去。我可以写一个自定义的。或者我可以分两步转换它。知道我想使用 Ramda 的 zipObj function, I can first convert the above to arrays via toPairs,生成这个:

[
  ["set1", ["option1", "option2", "option3"]], 
  ["set2", ["optionA", "optionB"]]
]

然后我可以使用我希望每个 属性 拥有的键将 zipObj 映射到结果上。这意味着我可以调用 map(zipObj(['set', 'options'])) 以获得最终期望的结果:

[
  {
    set: "set1",
    options: ["option1", "option2", "option3"]
  },
  {
    set: "set2",
    options: ["optionA", "optionB"]
  }
]

第 5 步:将它们放在一起

好吧,现在我们要把这些放在一起。 Ramda 有 pipe and compose。我通常只在适合一行时才选择 compose。所以将这些与 pipe 合并,我们可以这样写:

const transform = pipe(
  pluck('data'),
  groupBy(prop('set')),
  map(pluck('option')),
  toPairs,
  map(zipObj(['set', 'options']))
)

然后就叫它

transform(myObj)

您可以在 Ramda REPL 上看到这一点。在那里你可以注释掉 pipe 中后面的行,看看前面的行做了什么。

我在那里构建了代码,一次向管道添加一行,直到我转换了数据。我认为这是一种很好的工作方式。