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按嵌套值过滤对象数组的主要内容,如果未能解决你的问题,请参考以下文章

Ramda js:具有嵌套对象数组的深度嵌套对象的镜头

使用 Ramda 过滤每个内部属性数组

如何按名称过滤键,然后使用 jq 访问嵌套对象

使用 Ramda 在特定条件下添加对象属性值

使用 Javascript 根据深度值过滤嵌套对象

使用 Vue 过滤以检索嵌套对象中的特定值