如何使用 ramda.js 重构此代码?
How do I refactor this code using ramda.js?
我正在使用 mithril.js
和 ramda.js
。我在秘银中有以下代码:
return m('ul.list-inline.text-center', [
ctrl.items.map(item => R.allPass(item.policies)(item) ? m('li', m('a', {
href: item.uri,
config: m.route
}, item.name)) : null)
]);
所以基本上我是在遍历 ctrl.items
,而 item.policies
只是一个返回 true / false
的简单函数数组。因此,如果一切通过,R.allPass(item.policies)(item)
会给我 true
,否则会 false
,然后这决定是否呈现 li
标签。
因此,我认为创建这种更具功能性的方式会更好,因为我将能够重用代码,而不是使用此功能。所以我想我会 filter
使用那个 R.allPass
来代替地图,然后只渲染出过滤后的数组。
我开始在 ramda 中摆弄一个简单的过滤器,但我不知道如何实现结果:
var filterItems = R.filter(
R.pipe(
R.prop('policies'),
R.allPass
)
);
这只是 returns 完整数组。它根本不排除任何项目。
好吧,根据我对您的类型的了解,问题是您希望函数执行双重任务。
假设 items
包含如下对象列表:
const items = [
{
name: 'foo',
val: 13,
policies: [
obj => obj.val > 10,
obj => obj.val < 20,
obj => obj.val % 2 == 1,
]
},
{
name: 'bar',
val: 14,
policies: [
obj => obj.val > 10,
obj => obj.val < 20,
obj => obj.val % 2 == 1,
]
}
];
然后这个:
R.pipe(
R.prop('policies'),
R.allPass
)
将有一个类似这样的类型:
const testPolicies = R.pipe(
R.prop('policies'), // :: Item -> [(Item -> Boolean)]
R.allPass // :: [(Item -> Boolean)] -> Item -> Boolean
) // => :: Item -> Item -> Boolean
您需要将项目的两个副本传递给此函数才能将其转换为 return 布尔值。但是你把它当作它只适用于一个,就好像它可以作为 filter :: (Item -> Boolean) -> [Item] -> [Item]
的输入一样。
您需要一种方法将您的 (Item -> Item -> Boolean)
转换为 (Item -> Boolean)
。
Ramda 中没有内置任何东西来帮助解决这个问题,尽管也许 Ramda 应该考虑添加它,因为它偶尔会有用。但是标准组合器之一 1,称为 W
会这样做:
const W = f => x => f(x)(x);
然后你可以像这样使用它:
R.filter(W(testPolicies), items); //=>
// {
// name: 'foo',
// val: 13,
// policies: [
// obj => obj.val > 10,
// obj => obj.val < 20,
// obj => obj.val % 2 == 1,
// ]
// }
您可以在 Ramda REPL.
上看到实际效果
1 Avaq's gist.
中有一个很好的组合器列表
我正在使用 mithril.js
和 ramda.js
。我在秘银中有以下代码:
return m('ul.list-inline.text-center', [
ctrl.items.map(item => R.allPass(item.policies)(item) ? m('li', m('a', {
href: item.uri,
config: m.route
}, item.name)) : null)
]);
所以基本上我是在遍历 ctrl.items
,而 item.policies
只是一个返回 true / false
的简单函数数组。因此,如果一切通过,R.allPass(item.policies)(item)
会给我 true
,否则会 false
,然后这决定是否呈现 li
标签。
因此,我认为创建这种更具功能性的方式会更好,因为我将能够重用代码,而不是使用此功能。所以我想我会 filter
使用那个 R.allPass
来代替地图,然后只渲染出过滤后的数组。
我开始在 ramda 中摆弄一个简单的过滤器,但我不知道如何实现结果:
var filterItems = R.filter(
R.pipe(
R.prop('policies'),
R.allPass
)
);
这只是 returns 完整数组。它根本不排除任何项目。
好吧,根据我对您的类型的了解,问题是您希望函数执行双重任务。
假设 items
包含如下对象列表:
const items = [
{
name: 'foo',
val: 13,
policies: [
obj => obj.val > 10,
obj => obj.val < 20,
obj => obj.val % 2 == 1,
]
},
{
name: 'bar',
val: 14,
policies: [
obj => obj.val > 10,
obj => obj.val < 20,
obj => obj.val % 2 == 1,
]
}
];
然后这个:
R.pipe(
R.prop('policies'),
R.allPass
)
将有一个类似这样的类型:
const testPolicies = R.pipe(
R.prop('policies'), // :: Item -> [(Item -> Boolean)]
R.allPass // :: [(Item -> Boolean)] -> Item -> Boolean
) // => :: Item -> Item -> Boolean
您需要将项目的两个副本传递给此函数才能将其转换为 return 布尔值。但是你把它当作它只适用于一个,就好像它可以作为 filter :: (Item -> Boolean) -> [Item] -> [Item]
的输入一样。
您需要一种方法将您的 (Item -> Item -> Boolean)
转换为 (Item -> Boolean)
。
Ramda 中没有内置任何东西来帮助解决这个问题,尽管也许 Ramda 应该考虑添加它,因为它偶尔会有用。但是标准组合器之一 1,称为 W
会这样做:
const W = f => x => f(x)(x);
然后你可以像这样使用它:
R.filter(W(testPolicies), items); //=>
// {
// name: 'foo',
// val: 13,
// policies: [
// obj => obj.val > 10,
// obj => obj.val < 20,
// obj => obj.val % 2 == 1,
// ]
// }
您可以在 Ramda REPL.
上看到实际效果1 Avaq's gist.
中有一个很好的组合器列表