我有一个像下面这样的对象数组,
[
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [],
"selectedValues": []
}
]
Run Code Online (Sandbox Code Playgroud)
此外,如果我迭代对象,对象中存在的“值”键,它会将其转换为如下所示,
[
{
"myValues": []
},
{
"myValues": [],
"values": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"availableValues": [
{"a": "x", "b": "1"},
{"a": "y", "b": "2"}
],
"selectedValues": ["x", "y"]
}
]
Run Code Online (Sandbox Code Playgroud)
我尝试过 ramda 函数式编程,但没有结果,
let findValues = x => {
if(R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
R.map(findValues, arrObj);
Run Code Online (Sandbox Code Playgroud)
首先,你需要小心你的动词。Ramda 不会修改您的数据结构。不变性是 Ramda 以及一般函数式编程的核心。如果您真的想改变您的数据,Ramda 将不是您的工具包。但是,Ramda 会转换它,并且它可以以相对内存友好的方式进行转换,重用数据结构中未转换的那些部分。
我们先来看看清理你函数中的几个问题:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
Run Code Online (Sandbox Code Playgroud)
第一个也是最明显的问题是这个函数不返回任何东西。如上所述,Ramda 不会改变您的数据。因此,当提供给 Ramda 的map. 我们可以通过返回map(evolve)调用结果并在if条件失败时返回原始值来解决此问题:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
接下来,map调用毫无意义。 evolve已经迭代了对象的属性。所以我们可以删除它。但我们还需要应用evolve到您的输入值:
let findValues = x => {
if (R.has('values')(x)){
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}, x)
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
还有一个问题。 Evolve期望包含转换值的函数的对象。例如,
evolve({
a: n => n * 10,
b: n => n + 5
})({a: 1, b: 2, c: 3}) //=> {a: 10, b: 7, c: 3}
Run Code Online (Sandbox Code Playgroud)
请注意,提供给对象的属性值evolve是函数。此代码正在提供值。我们可以换用这些值R.always,通过availableValues: R.always(availableValues),但我认为它更简单的使用lambda用的“参数_”,这意味着该参数是不重要的:availableValues: _ => availableValues。你也可以写availableValues: () => available values,但下划线表明通常的值被忽略的事实。
解决这个问题得到一个有效的功能:
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
}
Run Code Online (Sandbox Code Playgroud)
let findValues = x => {
if (R.has('values')(x)) {
let availableValues = R.prop('values')(x);
let selectedValues = R.pluck('a')(availableValues);
return R.map(R.evolve({
availableValues: availableValues,
selectedValues: selectedValues
}))
}
return x;
}
Run Code Online (Sandbox Code Playgroud)
如果您在此处运行此代码段,您还会看到一个有趣的点,即结果availableValues属性是对原始属性的引用values,而不是它的单独副本。这有助于表明 Ramda 正在重用原始数据。你也可以通过注意到这一点来看到这一点R.map(findValues, arrObj)[0] === arrObj[0] //=> true。
我写了我自己的版本,而不是从你的版本开始。这是一个工作功能:
const findValues = when(
has('values'),
x => evolve({
availableValues: _ => prop('values', x),
selectedValues: _ => pluck('a', prop('values', x))
}, x)
)
Run Code Online (Sandbox Code Playgroud)
请注意 的使用when,它捕获了“当此谓词对您的值成立时,使用以下转换;否则使用您的原始值”的概念。我们传递 predicate has('values'),本质上和上面一样。我们的转换与您的类似,使用evolve,但它跳过临时变量,以重复prop调用为代价。
关于这个版本,我仍然有些困扰。使用带有“ _”的那些 lambdas实际上是对evolve. 我认为这更清洁:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x),
selectedValues: pluck('a', prop('values', x))
})
)
Run Code Online (Sandbox Code Playgroud)
这里我们使用merge,它是 的纯函数版本Object.assign,将第二个对象的参数添加到第一个对象的参数,当它们冲突时替换。
这是我会选择的。
自从我开始写这个答案以来,斯科特克里斯托弗发布了另一个,基本上
const findValues = R.map(R.when(R.has('values'), R.applySpec({
myValues: R.prop('myValues'),
values: R.prop('values'),
availableValues: R.prop('values'),
selectedValues: R.o(R.pluck('a'), R.prop('values'))
})))
Run Code Online (Sandbox Code Playgroud)
这是一个很好的解决方案。它也是很好的无点。如果您的输入对象固定为这些属性,那么我会选择这个而不是我的merge版本。如果您的实际对象包含更多属性,或者只有在输入中才应包含输出中的动态属性,那么我会选择我的merge解决方案。关键是 Scott 的解决方案对于这个特定结构更清晰,但我的解决方案可以更好地扩展到更复杂的对象。
这不是一个非常复杂的转换,但它是不平凡的。构建这样的东西的一种简单方法是在非常小的阶段构建它。我经常在Ramda REPL 中这样做,这样我就可以看到结果。
我的过程看起来像这样:
确保我只正确转换了正确的值:
const findValues = when(
has('values'),
always('foo')
)
map(findValues, arrObj) //=> [{myValues: []}, "foo"]
Run Code Online (Sandbox Code Playgroud)
确保我的变压器功能具有正确的值。
const findValues = when(
has('values'),
keys
)
map(findValues, arrObj)
//=> [{myValues: []}, ["myValues", "values", "availableValues", "selectedValues"]]
Run Code Online (Sandbox Code Playgroud)
这里identity和keys. 任何证明传递了正确对象的东西都可以。
测试merge这里是否能正常工作。
const findValues = when(
has('values'),
x => merge(x, {foo: 'bar'})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// availableValues: [],
// selectedValues: [],
// foo: "bar"
// }
// ]
Run Code Online (Sandbox Code Playgroud)
所以我可以正确合并两个对象。请注意,这会保留我原始对象的所有现有属性。
现在,实际转换我想要的第一个值:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x)
})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// availableValues: [{a: "x", b: "1"}, {a: "y", b: "2"}],
// selectedValues: []
// }
// ]
Run Code Online (Sandbox Code Playgroud)
这就是我想要的。所以现在添加另一个属性:
const findValues = when(
has('values'),
x => merge(x, {
availableValues: prop('values', x),
selectedValues: pluck('a', prop('values', x))
})
)
map(findValues, arrObj)
//=> [
// {myValues: []},
// {
// myValues: [],
// values: [{a: "x", b: "1"}, a: "y", b: "2"}],
// availableValues: [{a: "x", b" "1"}, {a: "y", b: "2"}],
// selectedValues: ["x", "y"]
// }
// ]
Run Code Online (Sandbox Code Playgroud)
这最终给出了我想要的结果。
这是一个快速的过程。我可以在 REPL 中非常快速地运行和测试,迭代我的解决方案,直到我得到我想要的东西。