如何使用ramdajs同时更改多个值
How to change multiple values at the same time using ramdajs
我是 ramdajs 的新手。
假设我有一个对象:
{a: 1, b: 2, c: 3}
我可以这样做来将 a 更改为 11:
const aLens = R.lensProp('a');
R.set(aLens, 11, {a: 1, b: 2, c: 3});
如何通过一次调用将 a 更改为 11,将 b 更改为 22?
好吧,你真的不能用镜头来做。镜头专注于数据结构的单个部分。
Ramda 的 evolve
might do the trick:
const foo = {a: 1, b: 2, c: 3};
evolve({a: always(11), b: always(22)})(foo)// => {a: 11, b: 22, c: 3}
但我认为您需要考虑 "in a single call" 是什么意思。 Ramda 提供 compose
and pipe
(以及一些更晦涩的替代方案)以允许您从多个调用中构建单个函数。如果您调用生成的函数,您仍然在进行一次调用,但在非常真实的意义上,您正在调用管道中包含的所有函数。
因此,
const fn = pipe(
set(lensProp('a'), 11),
set(lensProp('b'), 22)
);
fn({a: 1, b: 2, c: 3}); //=> {a: 11, b: 22, c: 3}
然而那是 a "single call" 到 fn
。
如果您只需要用已知值更新键,一种选择是使用 R.merge
:
const obj = {a: 1, b: 2, c: 3}
R.merge(obj, {a: 11, b: 22}) //=> {"a": 11, "b": 22, "c": 3}
如果您需要根据对象的现有值更新对象,则可以改用 R.evolve
。
const abAdder = R.evolve({
a: add(10),
b: add(20)
})
abAdder(obj) //=> {"a": 11, "b": 22, "c": 3}
您也可以将其创建为镜头,但除非您计划将其与其他镜头组合,否则上述选项的优势值得怀疑。
const projectLens = keys => R.lens(R.pick(keys), R.flip(R.merge))
const abLens = projectLens(['a', 'b']);
R.set(abLens, { a: 11, b: 22 }, obj) //=> {"a": 11, "b": 22, "c": 3}
R.view(abLens, obj) //=> {"a": 1, "b": 2}
其实有一种方法可以用镜头做到这一点,但在这样的位置使用镜头可能不是最好的选择。
逻辑上R.set
returns 整个对象。所以它可以重复几次(链接)来执行你需要的任务:
const aLens = R.lensProp('a');
const bLens = R.lensProp('b');
R.set(bLens, 22)(R.set(aLens, 11)({a: 1, b: 2, c: 3}));
或者为了更好的可视化:
const aLens = R.lensProp('a');
const bLens = R.lensProp('b');
R.compose(R.set(bLens, 22), R.set(aLens,11))({a: 1, b: 2, c: 3});
您可以使用 merge 和 curry 来做到这一点:
curry(merge(__, {a:11, b:22}))({a:1, b:2, c:3});
我更喜欢制作这个 util 函数:
const overwrite = R.curry((over, org) => R.merge(R.__, over)(org));
overwrite({a:11, b:22}, {a:1, b:2, c:3});
直接来自食谱:
https://github.com/ramda/ramda/wiki/Cookbook#assocpaths
const obj = {a: 1, b: 2, c: 3};
const assocPaths = R.pipe(R.zipWith(R.assocPath), R.flip(R.reduce(R.applyTo)));
assocPaths(["a", "b"], [11, 22])(obj) //=> {"a": 11, "b": 22, "c": 3}
与 evolve 不同,assocPaths 无法根据现有值进行更新。但是,如果您的新值是静态的,那么 assocPaths 可能比为 R.evolve 创建转换函数更简单(见下文)。
const obj = {a: 1, b: 2, c: 3};
const newValues = [{id: "a", newValue: 11}, {id: "b", newValue: 22}];
const transformations = R.reduce((acc, curr) => {
return {
...acc,
[curr.id]: x => curr.newValue // or update based on current value
}
}, {}, newValues); // {{"a": x => 11}, {"b": x => 22}}
console.log(
R.evolve(transformations, obj)
); // { "a": 11, "b": 22, "c": 3}
<script src="http://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
我是 ramdajs 的新手。 假设我有一个对象:
{a: 1, b: 2, c: 3}
我可以这样做来将 a 更改为 11:
const aLens = R.lensProp('a');
R.set(aLens, 11, {a: 1, b: 2, c: 3});
如何通过一次调用将 a 更改为 11,将 b 更改为 22?
好吧,你真的不能用镜头来做。镜头专注于数据结构的单个部分。
Ramda 的 evolve
might do the trick:
const foo = {a: 1, b: 2, c: 3};
evolve({a: always(11), b: always(22)})(foo)// => {a: 11, b: 22, c: 3}
但我认为您需要考虑 "in a single call" 是什么意思。 Ramda 提供 compose
and pipe
(以及一些更晦涩的替代方案)以允许您从多个调用中构建单个函数。如果您调用生成的函数,您仍然在进行一次调用,但在非常真实的意义上,您正在调用管道中包含的所有函数。
因此,
const fn = pipe(
set(lensProp('a'), 11),
set(lensProp('b'), 22)
);
fn({a: 1, b: 2, c: 3}); //=> {a: 11, b: 22, c: 3}
然而那是 a "single call" 到 fn
。
如果您只需要用已知值更新键,一种选择是使用 R.merge
:
const obj = {a: 1, b: 2, c: 3}
R.merge(obj, {a: 11, b: 22}) //=> {"a": 11, "b": 22, "c": 3}
如果您需要根据对象的现有值更新对象,则可以改用 R.evolve
。
const abAdder = R.evolve({
a: add(10),
b: add(20)
})
abAdder(obj) //=> {"a": 11, "b": 22, "c": 3}
您也可以将其创建为镜头,但除非您计划将其与其他镜头组合,否则上述选项的优势值得怀疑。
const projectLens = keys => R.lens(R.pick(keys), R.flip(R.merge))
const abLens = projectLens(['a', 'b']);
R.set(abLens, { a: 11, b: 22 }, obj) //=> {"a": 11, "b": 22, "c": 3}
R.view(abLens, obj) //=> {"a": 1, "b": 2}
其实有一种方法可以用镜头做到这一点,但在这样的位置使用镜头可能不是最好的选择。
逻辑上R.set
returns 整个对象。所以它可以重复几次(链接)来执行你需要的任务:
const aLens = R.lensProp('a');
const bLens = R.lensProp('b');
R.set(bLens, 22)(R.set(aLens, 11)({a: 1, b: 2, c: 3}));
或者为了更好的可视化:
const aLens = R.lensProp('a');
const bLens = R.lensProp('b');
R.compose(R.set(bLens, 22), R.set(aLens,11))({a: 1, b: 2, c: 3});
您可以使用 merge 和 curry 来做到这一点:
curry(merge(__, {a:11, b:22}))({a:1, b:2, c:3});
我更喜欢制作这个 util 函数:
const overwrite = R.curry((over, org) => R.merge(R.__, over)(org));
overwrite({a:11, b:22}, {a:1, b:2, c:3});
直接来自食谱: https://github.com/ramda/ramda/wiki/Cookbook#assocpaths
const obj = {a: 1, b: 2, c: 3};
const assocPaths = R.pipe(R.zipWith(R.assocPath), R.flip(R.reduce(R.applyTo)));
assocPaths(["a", "b"], [11, 22])(obj) //=> {"a": 11, "b": 22, "c": 3}
与 evolve 不同,assocPaths 无法根据现有值进行更新。但是,如果您的新值是静态的,那么 assocPaths 可能比为 R.evolve 创建转换函数更简单(见下文)。
const obj = {a: 1, b: 2, c: 3};
const newValues = [{id: "a", newValue: 11}, {id: "b", newValue: 22}];
const transformations = R.reduce((acc, curr) => {
return {
...acc,
[curr.id]: x => curr.newValue // or update based on current value
}
}, {}, newValues); // {{"a": x => 11}, {"b": x => 22}}
console.log(
R.evolve(transformations, obj)
); // { "a": 11, "b": 22, "c": 3}
<script src="http://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>