如何在 ramda 中链接多个 "or" 或 "and" 语句并保留短路评估?
How to chain multiple "or" or "and" statements in ramda and preserve short-circuit evaluation?
我想根据用户定义的文本过滤 post 数组。每个 post 都有一个 id
和一个 text
属性 应该被搜索。如果搜索文本是一个空字符串,显然应该显示所有 posts - 无需检查其他谓词是否解析为真。目前我正在做这样的事情:
const hasText = R.curry((text, post) => R.reduce(R.or, false, [
R.equals("", text),
R.includes(text, post.text),
R.includes(text, post.id.toString())
]))
const posts = [{id: 1, text: "a"},{id: 2, text: "b"},{id: 3, text: "c"}]
console.log(R.filter(hasText("b"), posts));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
这里的问题是所有谓词都是预先评估的,即使这是不必要的。
是否可以使用 ramda 以更实用的方式实现与使用普通 ||
相同的效果?
如果文本为空字符串,则根本不需要对数组进行过滤。要检查任何谓词是否为真,您可以使用 R.any:
const { curry, pipe, prop, toString, includes, filter, anyPass, isEmpty } = R
// check if prop includes text
const propIncludes = curry((key, text) => pipe(prop(key), toString, includes(text)))
// filter an array of posts, and check if any of the prop contains the text
const hasText = curry((text, posts) => filter(anyPass([
propIncludes('text', text),
propIncludes('id', text),
]))(posts))
// skips filtering if text is empty
const checkText = curry((text, posts) => isEmpty(text) ? posts : hasText(text, posts))
const posts = [{id: 1, text: "a"},{id: 2, text: "b"},{id: 3, text: "c"}]
const result = checkText('b', posts)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
您可以以类似于其他答案的 propIncludes
函数的方式使用 built-in R.propSatisfies
函数:
const textContains = curry(
(srch, value) => pipe(toString, includes(srch))(value)
)
const postHasText = curry(
(text, post) => anyPass([
propSatisfies(textContains(text), 'text'),
propSatisfies(textContains(text), 'id')
])(post)
)
const postsWithText = curry(
(text, posts) => filter(postHasText(text), posts)
)
或者,如下例所示,你可以制作一个中间辅助方法 propsContain
然后 postHasText
更简单一些,你可以将 propsContain
重用于其他东西也是。
const { curry, pipe, toString, props, includes, filter, any, isEmpty } = R
const textContains = curry(
(srch, value) => pipe(toString, includes(srch))(value)
)
const propsContain = curry(
(srch, propList, entity) => pipe(props(propList), any(textContains(srch)))(entity)
)
const postHasText = propsContain(R.__, ['id', 'text'], R.__)
const searchPosts = curry(
(qry, posts) => isEmpty(qry) ? posts : filter(postHasText(qry), posts)
)
const posts = [{id: "a", text: "foo"},{id: 2, text: "box"},{id: 3, text: "bat"}]
const results = searchPosts('a', posts)
console.log(results)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
我想根据用户定义的文本过滤 post 数组。每个 post 都有一个 id
和一个 text
属性 应该被搜索。如果搜索文本是一个空字符串,显然应该显示所有 posts - 无需检查其他谓词是否解析为真。目前我正在做这样的事情:
const hasText = R.curry((text, post) => R.reduce(R.or, false, [
R.equals("", text),
R.includes(text, post.text),
R.includes(text, post.id.toString())
]))
const posts = [{id: 1, text: "a"},{id: 2, text: "b"},{id: 3, text: "c"}]
console.log(R.filter(hasText("b"), posts));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
这里的问题是所有谓词都是预先评估的,即使这是不必要的。
是否可以使用 ramda 以更实用的方式实现与使用普通 ||
相同的效果?
如果文本为空字符串,则根本不需要对数组进行过滤。要检查任何谓词是否为真,您可以使用 R.any:
const { curry, pipe, prop, toString, includes, filter, anyPass, isEmpty } = R
// check if prop includes text
const propIncludes = curry((key, text) => pipe(prop(key), toString, includes(text)))
// filter an array of posts, and check if any of the prop contains the text
const hasText = curry((text, posts) => filter(anyPass([
propIncludes('text', text),
propIncludes('id', text),
]))(posts))
// skips filtering if text is empty
const checkText = curry((text, posts) => isEmpty(text) ? posts : hasText(text, posts))
const posts = [{id: 1, text: "a"},{id: 2, text: "b"},{id: 3, text: "c"}]
const result = checkText('b', posts)
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
您可以以类似于其他答案的 propIncludes
函数的方式使用 built-in R.propSatisfies
函数:
const textContains = curry(
(srch, value) => pipe(toString, includes(srch))(value)
)
const postHasText = curry(
(text, post) => anyPass([
propSatisfies(textContains(text), 'text'),
propSatisfies(textContains(text), 'id')
])(post)
)
const postsWithText = curry(
(text, posts) => filter(postHasText(text), posts)
)
或者,如下例所示,你可以制作一个中间辅助方法 propsContain
然后 postHasText
更简单一些,你可以将 propsContain
重用于其他东西也是。
const { curry, pipe, toString, props, includes, filter, any, isEmpty } = R
const textContains = curry(
(srch, value) => pipe(toString, includes(srch))(value)
)
const propsContain = curry(
(srch, propList, entity) => pipe(props(propList), any(textContains(srch)))(entity)
)
const postHasText = propsContain(R.__, ['id', 'text'], R.__)
const searchPosts = curry(
(qry, posts) => isEmpty(qry) ? posts : filter(postHasText(qry), posts)
)
const posts = [{id: "a", text: "foo"},{id: 2, text: "box"},{id: 3, text: "bat"}]
const results = searchPosts('a', posts)
console.log(results)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>