Ramda — 根据允许列表切换对象的属性
Ramda — Toggle properties of an object based on an allow list
我有以下一个对象和一个数组的情况:
const currenObject = {
errors: false,
values: false,
warnings: false,
};
const template = ['values', 'warnings'];
如何映射 currentObject
并且如果 属性 存在于 template
数组中以将相应的 属性 更改为 true
。所以上面的例子应该产生:
const currenObject = {
errors: false,
values: true,
warnings: true,
};
这对于 vanilla javascript 来说是微不足道的,但我们的目标是通过 Ramda JS 来实现它。
提前谢谢你。
将 template
映射到 [key, T]
的数组,使用 R.fromPairs 转换为对象,然后使用 R.evolve 创建更新的对象:
const { pipe, map, of, append, T, fromPairs, evolve } = R;
const fn = pipe(map(pipe(of, append(T))), fromPairs, evolve);
const currenObject = {
errors: false,
values: false,
warnings: false,
};
const template = ['values', 'warnings'];
const result = fn(template)(currenObject);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
鉴于:
const obj = { errors: false, values: false, warnings: false };
const tpl = ['values', 'warnings'];
您可以从 tpl
中挑选 obj
中的属性:
pick(tpl, obj);
//=> {"values": false, "warnings": false}
并将它们设置为 true
:
map(T, pick(tpl, obj));
//=> {"values": true, "warnings": true}
剩下的就是将该对象合并到原始对象中:
const toggle = curry((t, o) => mergeRight(o, map(T, pick(t, o))));
toggle(tpl, obj);
//=> {"errors": false, "values": true, "warnings": true}
其他答案假定您要将相关值设置为 true
。
但标题暗示你想切换它们当前的布尔值。这是一个这样做的版本,如果我将其中一个输入的目标值切换为以 true
.
开头,这将更加明显
const toggleFields = compose (evolve, compose (mergeAll, map (objOf (__, not))))
const currentObject = {
errors: false,
values: true,
warnings: false,
};
const template = ['values', 'warnings'];
console .log (toggleFields (template) (currentObject))
// => {
// errors: false, // ignored
// values: false, // toggled from true
// warnings: true // toggled from false
// }
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script>const {compose, evolve, mergeAll, map, objOf, __, not} = R </script>
我们的工作是将['values', 'warnings']
变成
evolve ({
values: not,
warnings: not
})
其中 not
is Ramda's boolean-toggle function and evolve
采用配置 object 将名称映射到转换器函数和 returns 一个采用 object 并克隆它的函数,转换任何指定的属性通过适当的功能。然后我们简单地用我们的源调用这个函数 object.
如果正好遇到字符荒,可以用Ramda curried,二元组合函数,o
, in place of the venerable compose
本身:
const toggleFields = o (evolve, o (mergeAll, map (objOf (__, not))))
根据口味,我们也可以将 objOf (__, not)
替换为 flip (objOf) (not)
。
推导
这个版本看起来很密集。我原来是这样写的:
const fn = (template) => (currentObject) => evolve (pipe (
map (objOf (__, not)),
mergeAll
) (template)) (currentObject)
但是在这里,如果我们稍微眯起眼睛,我们可以看到这个形式:
(x) => (y) => f (g (x)) (y)
其中x
代表template
,y
代表currentObject
,f
代表evolve
,g
代表其他一切。
并且当我们有一个仅在 body 末尾应用的最终参数时,我们可以简单地删除它,因此与
相同
(x) => f (g (x))
但这只是f
和g
的组合。所以我们可以写成
compose (f, g)
并替换回原来的我们得到
compose (
evolve,
pipe (map (objOf (__, not)), mergeAll)
)
我通常不喜欢在管道中混合使用 compose
和 pipe
。我对 one-liners 使用 compose
,对更长的函数使用 pipe
;这对我来说开始看起来像 one-liner。所以我们交换 map
和 mergeAll
的顺序并切换到 compose
,产生这个:
compose (evolve, compose (mergeAll, map (objOf (__, not)))
原版
作为 Ramda 的创始人和主要作者,我实际上是经常建议人们试图将它应用到他们不需要的地方的人。但在这里我认为这个版本比我提出的第一个 vanilla JS 版本更干净:
const toggle = (template) => (currentObject) =>
Object .fromEntries (
Object .entries (currentObject)
.map (([k, v]) => [k, template .includes (k) ? !v : v] )
)
并不是说这样不好,我不会为了这个小小的改进而引入Ramda。但如果我已经在使用 Ramda,那么 Ramda 版本似乎确实有所改进。
有模块
这是解决问题的基于模块的方法 -
// obj.js
function get (t, k) {
return t[k]
}
function set (t, k, v) {
return { ...t, [k]: v }
}
function update (t, k, f) {
return set(t, k, f(get(t, k)))
}
export { get, set, update }
我们建立了与对象交互的通用方式。现在我们可以使用 update
-
轻松编写 toggle
// main.js
import { update } from "./obj.js"
function toggle (t, k) {
return update(t, k, (v = false) => !v)
}
const input =
{ errors: false, values: false, warnings: false }
const output =
["values", "warnings"].reduce(toggle, input)
console.log(output)
{
errors: false,
values: true,
warnings: true
}
展开下面的代码片段以在浏览器中验证结果 -
function get (t, k) {
return t[k]
}
function set (t, k, v) {
return { ...t, [k]: v }
}
function update (t, k, f) {
return set(t, k, f(get(t, k)))
}
function toggle (t, k) {
return update(t, k, v => !v)
}
const input =
{ errors: false, values: false, warnings: false }
const output =
["values", "warnings"].reduce(toggle, input)
console.log(output)
与 ramda
如果您更喜欢 Ramda,则有完全(或接近)等价物 -
知道这一点,我们可以选择直接使用 ramda 或者我们可以使用 ramda 本身实现 obj
模块 -
// obj
import { assoc, prop, evolve } from "ramda"
function get (t, k) {
return prop(k, t)
}
function set (t, k, v) {
return assoc(k, v, t)
}
function update (t, k, f) {
return evolve({ [k]: f }, t)
}
export { get, set, update }
这里的区别是 obj
的用户不知道 ramda 正在幕后使用。这是设计自定义模块的有用方法,可以更有效地适合您的程序或模式,同时利用其他库或模式的优势。
我有以下一个对象和一个数组的情况:
const currenObject = {
errors: false,
values: false,
warnings: false,
};
const template = ['values', 'warnings'];
如何映射 currentObject
并且如果 属性 存在于 template
数组中以将相应的 属性 更改为 true
。所以上面的例子应该产生:
const currenObject = {
errors: false,
values: true,
warnings: true,
};
这对于 vanilla javascript 来说是微不足道的,但我们的目标是通过 Ramda JS 来实现它。 提前谢谢你。
将 template
映射到 [key, T]
的数组,使用 R.fromPairs 转换为对象,然后使用 R.evolve 创建更新的对象:
const { pipe, map, of, append, T, fromPairs, evolve } = R;
const fn = pipe(map(pipe(of, append(T))), fromPairs, evolve);
const currenObject = {
errors: false,
values: false,
warnings: false,
};
const template = ['values', 'warnings'];
const result = fn(template)(currenObject);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
鉴于:
const obj = { errors: false, values: false, warnings: false };
const tpl = ['values', 'warnings'];
您可以从 tpl
中挑选 obj
中的属性:
pick(tpl, obj);
//=> {"values": false, "warnings": false}
并将它们设置为 true
:
map(T, pick(tpl, obj));
//=> {"values": true, "warnings": true}
剩下的就是将该对象合并到原始对象中:
const toggle = curry((t, o) => mergeRight(o, map(T, pick(t, o))));
toggle(tpl, obj);
//=> {"errors": false, "values": true, "warnings": true}
其他答案假定您要将相关值设置为 true
。
但标题暗示你想切换它们当前的布尔值。这是一个这样做的版本,如果我将其中一个输入的目标值切换为以 true
.
const toggleFields = compose (evolve, compose (mergeAll, map (objOf (__, not))))
const currentObject = {
errors: false,
values: true,
warnings: false,
};
const template = ['values', 'warnings'];
console .log (toggleFields (template) (currentObject))
// => {
// errors: false, // ignored
// values: false, // toggled from true
// warnings: true // toggled from false
// }
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script>const {compose, evolve, mergeAll, map, objOf, __, not} = R </script>
我们的工作是将['values', 'warnings']
变成
evolve ({
values: not,
warnings: not
})
其中 not
is Ramda's boolean-toggle function and evolve
采用配置 object 将名称映射到转换器函数和 returns 一个采用 object 并克隆它的函数,转换任何指定的属性通过适当的功能。然后我们简单地用我们的源调用这个函数 object.
如果正好遇到字符荒,可以用Ramda curried,二元组合函数,o
, in place of the venerable compose
本身:
const toggleFields = o (evolve, o (mergeAll, map (objOf (__, not))))
根据口味,我们也可以将 objOf (__, not)
替换为 flip (objOf) (not)
。
推导
这个版本看起来很密集。我原来是这样写的:
const fn = (template) => (currentObject) => evolve (pipe (
map (objOf (__, not)),
mergeAll
) (template)) (currentObject)
但是在这里,如果我们稍微眯起眼睛,我们可以看到这个形式:
(x) => (y) => f (g (x)) (y)
其中x
代表template
,y
代表currentObject
,f
代表evolve
,g
代表其他一切。
并且当我们有一个仅在 body 末尾应用的最终参数时,我们可以简单地删除它,因此与
相同(x) => f (g (x))
但这只是f
和g
的组合。所以我们可以写成
compose (f, g)
并替换回原来的我们得到
compose (
evolve,
pipe (map (objOf (__, not)), mergeAll)
)
我通常不喜欢在管道中混合使用 compose
和 pipe
。我对 one-liners 使用 compose
,对更长的函数使用 pipe
;这对我来说开始看起来像 one-liner。所以我们交换 map
和 mergeAll
的顺序并切换到 compose
,产生这个:
compose (evolve, compose (mergeAll, map (objOf (__, not)))
原版
作为 Ramda 的创始人和主要作者,我实际上是经常建议人们试图将它应用到他们不需要的地方的人。但在这里我认为这个版本比我提出的第一个 vanilla JS 版本更干净:
const toggle = (template) => (currentObject) =>
Object .fromEntries (
Object .entries (currentObject)
.map (([k, v]) => [k, template .includes (k) ? !v : v] )
)
并不是说这样不好,我不会为了这个小小的改进而引入Ramda。但如果我已经在使用 Ramda,那么 Ramda 版本似乎确实有所改进。
有模块
这是解决问题的基于模块的方法 -
// obj.js
function get (t, k) {
return t[k]
}
function set (t, k, v) {
return { ...t, [k]: v }
}
function update (t, k, f) {
return set(t, k, f(get(t, k)))
}
export { get, set, update }
我们建立了与对象交互的通用方式。现在我们可以使用 update
-
toggle
// main.js
import { update } from "./obj.js"
function toggle (t, k) {
return update(t, k, (v = false) => !v)
}
const input =
{ errors: false, values: false, warnings: false }
const output =
["values", "warnings"].reduce(toggle, input)
console.log(output)
{
errors: false,
values: true,
warnings: true
}
展开下面的代码片段以在浏览器中验证结果 -
function get (t, k) {
return t[k]
}
function set (t, k, v) {
return { ...t, [k]: v }
}
function update (t, k, f) {
return set(t, k, f(get(t, k)))
}
function toggle (t, k) {
return update(t, k, v => !v)
}
const input =
{ errors: false, values: false, warnings: false }
const output =
["values", "warnings"].reduce(toggle, input)
console.log(output)
与 ramda
如果您更喜欢 Ramda,则有完全(或接近)等价物 -
知道这一点,我们可以选择直接使用 ramda 或者我们可以使用 ramda 本身实现 obj
模块 -
// obj
import { assoc, prop, evolve } from "ramda"
function get (t, k) {
return prop(k, t)
}
function set (t, k, v) {
return assoc(k, v, t)
}
function update (t, k, f) {
return evolve({ [k]: f }, t)
}
export { get, set, update }
这里的区别是 obj
的用户不知道 ramda 正在幕后使用。这是设计自定义模块的有用方法,可以更有效地适合您的程序或模式,同时利用其他库或模式的优势。