Reputation: 7739
I am looking to sort an array of nested object using Ramda. I have an array:
const people = [
{ name: 'Emma', data:{ age: 70 }},
{ name: 'Peter', data:{ age: 78 }},
{ name: 'Mikhail', data:{ age: 62 }},
];
I want to sort above array using Ramda. I got this but it does not work for me.
Your help will be highly appreciated.
Upvotes: 1
Views: 3637
Reputation: 191986
Use R.path to get the data.age
:
const sortByYoungest = R.sortBy(R.path(['data', 'age']))
const people = [{"name":"Emma","data":{"age":70}},{"name":"Peter","data":{"age":78}},{"name":"Mikhail","data":{"age":62}}];
const result = sortByYoungest(people);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>
Taking a page from Nick's answer you can create a reusable sortByPath
function using R.pipe:
const sortByPath = R.pipe(R.path, R.sortBy)
const sortByYoungest = sortByPath(['data', 'age'])
const people = [{"name":"Emma","data":{"age":70}},{"name":"Peter","data":{"age":78}},{"name":"Mikhail","data":{"age":62}}];
const result = sortByYoungest(people);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>
Upvotes: 4
Reputation: 50759
As you want to sort by the path data.age
, you can get the prop data
from your object using R.prop
which will give you an object, and then use R.prop()
again on that object to get the age
property. To make a function which does this, you can use R.compose()
:
const byAge = R.ascend(R.compose(R.prop('age'), R.prop('data')));
const people = [
{ name: 'Emma', data:{ age: 70 }},
{ name: 'Peter', data:{ age: 78 }},
{ name: 'Mikhail', data:{ age: 62 }},
];
const peopleByYoungestFirst = R.sort(byAge, people);
console.log(peopleByYoungestFirst);
//=> [{"name":"Mikhail","data":{"age":62}},{"name":"Emma","data":{"age":70}},{"name":"Peter","data":{"age":78}}]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>
To break down the above compose function, say you have the object obj
, which is:
obj = { name: 'Emma', data:{ age: 70 }}
Performing R.prop('data')(obj)
will give:
{ age: 70 }
As you are interested in the age
property of the returned object, you again can run R.prop('age')
on the above object:
R.prop('age')({age: 70})
This will give 70
. So above line can be written as:
R.prop('age')(R.prop('data')(obj))
^^^^^ f ^^^^^ ^^^^^^ g ^^^^^ ^ x
The issue with this function, however, is that it doesn't return a function which we can pass obj
into to get 70
out of. Currently, it takes the form of f(g(x))
, by composing it, we can get the form of (f.g)(x)
, where f.g
composes the functions f
and g
to produce a new function. To compose in Ramda, we can use R.compose()
:
R.compose(R.prop('age'), R.prop('data'))(obj)
This can be eta-reduced to remove the obj
like it is in the example.
This approach can be generalized to:
const {pipe, split, reverse, map, apply, compose, ascend} = R;
const makeSortFn = compose(ascend, pipe(split('.'), reverse, map(R.prop), apply(compose)));
const byAge = makeSortFn('data.age');
const people = [
{ name: 'Emma', data:{ age: 70 }},
{ name: 'Peter', data:{ age: 78 }},
{ name: 'Mikhail', data:{ age: 62 }},
];
const peopleByYoungestFirst = R.sort(byAge, people);
console.log(peopleByYoungestFirst);
//=> [{"name":"Mikhail","data":{"age":62}},{"name":"Emma","data":{"age":70}},{"name":"Peter","data":{"age":78}}]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js" integrity="sha256-buL0byPvI/XRDFscnSc/e0q+sLA65O9y+rbF+0O/4FE=" crossorigin="anonymous"></script>
But instead, I would favour Ori's approach, which can be genralized much easier by splitting a string:
R.sortBy(R.path(path_str.split('.')))
Upvotes: 3