Saurabh Agrawal
Saurabh Agrawal

Reputation: 7739

Ramda sort array of nested object

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

Answers (2)

Ori Drori
Ori Drori

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

Nick Parsons
Nick Parsons

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

Related Questions