sadrzadehsina
sadrzadehsina

Reputation: 1349

Ramda sort with order and case insensitive

I am in a situation where I need to sort an array of objects descending or ascending while the result is case insensitive.

I searched and find a few functional ways of sorting ascending/descending. But what I need is a combination of order + case insensitivity.

Here is my array object.

const arr = [{ title: 'AAA' }, { title: 'BBB' }, { title: 'aaa' }];

What I need is a curried function which takes a sort config and do sort the given array based on that config.

const ascending = sort({ by: 'title', order: 'asc' })(arr);
// output: [{ title: 'AAA' }, { title: 'aaa' }, { title: 'BBB' }]
const descending = sort({ by: 'title', order: 'desc' })(arr);
// output: [{ title: 'BBB' }, { title: 'aaa' }, { title: 'AAA' }]

I have already implemented a version of my sort function. However I can't make it case insensitive.

const sortDirection = R.ifElse(
  R.equals('descending'), 
  R.always(R.descend), 
  R.always(R.ascend)
);
const sort = config => items => R.sortWith(
  [ 
    R.compose(
      sortDirection(config.order), 
      R.prop
    )(config.by) 
  ], 
  items
);

Upvotes: 0

Views: 813

Answers (2)

sadrzadehsina
sadrzadehsina

Reputation: 1349

based on @Scott Sauyet answer, I changed it a little bit so it looks like this.

const makeSorter = (config) => sort(
  (config.order === 'desc' ? descend : ascend) (
    compose (
      ifElse(is(String), toLower, identity),
      prop(config.by)
    )
  )
);

I just want to make it lower case when dealing with a string.

Upvotes: 0

Scott Sauyet
Scott Sauyet

Reputation: 50797

An answer from another question can easily be adapted to this:

const arr = [{ title: 'BBB' }, { title: 'AAA' }, { title: 'aaa' }]

const makeSorter = ({order, isNumber, by}) => sort (
  (order === 'desc' ? descend : ascend) ( compose (
    isNumber ? Number.parseFloat : toLower,
    prop (by)
  ))
)

console .log (
  makeSorter ({ by: 'title', order: 'asc' }) (arr)
) //~> [{ title: 'AAA' }, { title: 'aaa' }, { title: 'BBB' }]

console .log (
  makeSorter ({ by: 'title', order: 'desc' }) (arr)
) //~> [{ title: 'BBB' }, { title: 'AAA' }, { title: 'aaa' }]
.as-console-wrapper {min-height: 100%; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {sort, descend, ascend, compose, identity, toLower, prop } = R </script>

If you wanted to include a configuration property to determine sensitivity, you could just add it like this:

const makeSorter = ({order, isNumber, by, insensitive}) => sort (
  (order === 'desc' ? descend : ascend) ( compose (
    isNumber ? Number.parseFloat : insensitive ? toLower : identity,
    R.prop (by)
  ))
)

// later
makeSorter ({ by: 'title', order: 'asc', insensitive: true }) (arr)
//--------------------------------------------^

(Note that neither of these will actually capture your expected output exactly if given titles of 'aaa', 'AAA', and 'BBB' in any order, since modern sorts are stable, they would both sort 'aaa' and 'AAA' in their original array order, but your two outputs have them swapped.)

Upvotes: 2

Related Questions