使用 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 还提供了一个以这种方式组合 map
和 prop
的函数: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
。看起来我们可以在 set1
和 set2
上使用它。好吧 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
中后面的行,看看前面的行做了什么。
我在那里构建了代码,一次向管道添加一行,直到我转换了数据。我认为这是一种很好的工作方式。
给定一个包含这种形式的对象的数组:
[{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 还提供了一个以这种方式组合 map
和 prop
的函数: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
。看起来我们可以在 set1
和 set2
上使用它。好吧 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
中后面的行,看看前面的行做了什么。
我在那里构建了代码,一次向管道添加一行,直到我转换了数据。我认为这是一种很好的工作方式。