Reputation: 3494
I would like to filter an array of posts against a user defined text. Each post has an id
and a text
property which should be searched. If the search-text is an empty string all posts should obviously be displayed - no need to check if the other predicates resolve to true. Currently I'm doing something like this:
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>
The problem here is that all of the predicates are evaluated upfront even though that is unneccessary.
Is it possible to achieve the same effect as using plain ||
in a more functional way using ramda?
Upvotes: 1
Views: 841
Reputation: 6631
You can use the built-in R.propSatisfies
function in a manner similar to the other answer's propIncludes
function:
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)
)
Alternatively, as shown in the example below, you can make an intermediate helper method propsContain
and then postHasText
is a bit simpler, and you can reuse the propsContain
for other things too.
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>
Upvotes: 2
Reputation: 192422
If the text is an empty string, you don't need to filter the array at all. To check if any of the predicates is truthy, you can use 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>
Upvotes: 2