markdown 使用ramda按嵌套值过滤对象数组
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown 使用ramda按嵌套值过滤对象数组相关的知识,希望对你有一定的参考价值。
Say we have a prop.users of the shape:
```
const users = [
{username: 'bob', age: 30, tags: [{name: 'work', id: 1}, {name: 'boring', id: 2}]},
{username: 'jim', age: 25, tags: [{name: 'home', id: 3}, {name: 'fun', id: 4}]},
{username: 'jane', age: 30, tags: [{name: 'vacation', id: 5}, {name: 'fun', id: 4}]}
];
```
if you want to filter by `username` or `age` it's quite strait forward:
`R.filter(R.propEq('username', 'jane'))(data);`
things get tricky when you have something like `tags`
and you want to filter all users with `name: 'fun'`
## TLDR ##
this is the solution:
```
const hasFunTag = R.any(R.propEq('name', 'fun'))
R.filter(R.compose(hasFunTag, R.prop('tags')))(users)
```
## This was how i got there ##
my first approach to solve this was:
`R.filter(R.where({tags: R.contains({name:'fun'})}))(data);`
which didn't work because the `tags` array can contain other properties like `id` in our case.
this is consistent with REST APIs responses.
you could solve it by providing the `id` too:
`R.filter(R.where({tags: R.contains({name:'fun', id: 4})}))(data);`
but most times you only know `name` and if you want to write a function and provide the filter as argument, you are stuck.
brains tend to jump to solutions by resorting to what they already know. I knew i could solve my problem if i removed `id` from the array and that was something i knew how to do:
```
const changeTags = (tag) => R.project(['name'], tag)
const extract = users.map(user => ({...user, tags: changeTags(user.tags)}))
R.filter(R.where({tags: R.contains({name:'fun'})}))(extract);
```
Let's see what happens here:
- `R.project` helps you pick the properties you need from the given `tag` object.
- use spread operator and change `tags` based on function above.
- now we can filter as intended.
It solves the querying issue i had but it also transforms the initial `users` array and if your code uses somehow the `id` in the tags array or you typed your `tags` array, you are stuck.
this led me to think the brain needed to learn something new. I asked ramda and FP master [@asharif](https://medium.com/@sharifsbeat) and my React Vienna mentor to help me out of my mental paradigm. I asked him if ramda can solve this without modifying the initial array. 15' later here comes the other way of thinking my brain needed.
```
const hasFunTag = R.any(R.propEq('name', 'fun'))
R.filter(R.compose(hasFunTag, R.prop('tags')))(users)
```
it seems like all we needed was a `R.any` and a `R.compose` in the mix.
I'm gonna try an explanation:
1. `R.filter` takes the `users` array and looks at each object inside.
2. `R.compose` in ramda executes from right to left so
3. We need the path inside the object where `hasFunTag` function should look. `R.prop('tags', data[0])` lets say, returns `[{"id": 1, "name": "work"}, {"id": 2, "name": "boring"}]` .
4. `R.any(R.propEq('name', 'fun'))(of above [] result)` returns either `true` or `false` depending on whether there is a `name` key with value `fun`. For the above array it returns false. `R.any` helps us out of the _'removing keys before checking'_ issue i talked about before.
5. This response in turn becomes the filter predicate and removes the first object from the `users` array.
This opens up the possibility for further abstraction of course. I'd like to explore that in the future.
以上是关于markdown 使用ramda按嵌套值过滤对象数组的主要内容,如果未能解决你的问题,请参考以下文章