Raul Špilev
Raul Špilev

Reputation: 378

Case insensitive sorting with ramda sortWith

I'm having trouble sorting a deeply nested array of objects using Ramda sortWith by multiple columns (asceding or descending). Apparently the sorting is case sensitive and leads to values starting with lower case letters being put to the very end of the sorted array.

Firstly I import the necessary ramda functions:

import { sortWith, path, useWith, split, ascend, descend } from 'ramda'

Then I have this array:

const theList = [
  {
    name: 'A',
    purchase: {
      period: {
        start: '2020-01-08T21:00:00Z',
        end: '3070-10-27T21:00:00Z',
      },
    },
  },
  {
    name: 'b',
    purchase: {
      period: {
        start: '2019-09-30T19:00:00Z',
        end: '3070-10-27T21:00:00Z',
      },
    },
  },
  {
    name: 'C',
    purchase: {
      period: {
        start: '2020-01-26T21:00:00Z',
        end: '3070-10-27T21:00:00Z',
      },
    },
  },
]

For sorting I have this array with rules:

const sort = [
  { oder: 'asc', name: 'name' },
  { oder: 'desc', name: 'purchase.period.start' },
]

Finally I try to sort this array with the given ruleset:

const sortFunctions = sort.map(({ order, name }) => (
  (order === 'asc')
    ? ascend(useWith(path, [split('.')])(name))
    : descend(useWith(path, [split('.')])(name))
))

return sortWith(sortFunctions)(theList)

However the result is the following:

[
  {
    "name": "b",
    "purchase": {
      "period": {
        "start": "2019-09-30T19:00:00Z",
        "end": "3070-10-27T21:00:00Z"
      }
    }
  },
  {
    "name": "C",
    "purchase": {
      "period": {
        "start": "2020-01-26T21:00:00Z",
        "end": "3070-10-27T21:00:00Z"
      }
    }
  },
  {
    "name": "A",
    "purchase": {
      "period": {
        "start": "2020-01-08T21:00:00Z",
        "end": "3070-10-27T21:00:00Z"
      }
    }
  }
]

So the problem here is that b is not in the middle as expected, because it is taking case sensitivity into account. Not only that, but the list also appears to be in reverse order from the expected order.

What am I doing wrong here and how could I get this to ignore case sensitivity?

Upvotes: 0

Views: 898

Answers (1)

Scott Sauyet
Scott Sauyet

Reputation: 50797

With a slightly altered version of your code (using a pipeline rather than useWith, which I prefer to avoid as best I can), we can add toLower inside the comparator like this:

const makeSorter = compose (
  sortWith,
  map (({order, name}) => (order == 'desc' ? descend : ascend) (
    compose (toLower, path (split ('.', name)))
  ))
)

const sorts = [
  { order: 'asc', name: 'name' },
  { order: 'desc', name: 'purchase.period.start' },
]

// Switched the order to make the sorting clear
const theList = [{name: "C", purchase: {period: {start: "2020-01-26T21: 00: 00Z", end: "3070-10-27T21: 00: 00Z"}}}, {name: "A", purchase: {period: {start: "2020-01-08T21: 00: 00Z", end: "3070-10-27T21: 00: 00Z"}}}, {name: "b", purchase: {period: {start: "2019-09-30T19: 00: 00Z", end: "3070-10-27T21: 00: 00Z"}}}]

console .log (
  makeSorter (sorts) (theList)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {compose, sortWith, map, ascend, descend, toLower, path, split} = R </script>

I also changed the test from asc to desc because I've worked with something similar where asc was the default and the field was optional. (desc was mandatory when it was needed.)

This just avoids some repetition in the functions we pass to ascend or descend:

    (order == 'desc' ? descend : ascend)

I haven't tested that this actually works for multiple sorts, as these values all disagree on 'name', but it looks like it should work.

Upvotes: 1

Related Questions