如何在 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>