Say I have a list of strings and I want to sort them first by string length and then alphabetically (so strings within the list with the same length are sorted alphabetically).
I already have ordString
which can be used to sort alphabetically. I can make my own Ord
instance for string length:
import * as S from 'fp-ts/string'
import * as N from 'fp-ts/number'
import * as Ord from 'fp-ts/Ord'
const ordString = S.Ord
const ordStringLength = pipe(N.Ord, Ord.contramap((s: string) => s.length))
If I want to use these Ord
s to sort my list I can call A.sort
twice, but this seems wasteful:
import * as A from 'fp-ts/Array'
const sorted = pipe(listOfStrings, A.sort(ordString), A.sort(ordStringLength))
Is there a way I can compose the ordString
and ordStringLength
instances into a single instance that can be used in a single call to A.sort
?
(this is a contrived example to demonstate my question; in reality the ordering is more complex and may involve combining several Ord
s)
CodePudding user response:
Ord
has a Monoid
instance, which can be used to do what you want; that is, first order with one Ord
, then with another.
There's a pretty good example in the fp-ts
docs
in short, you should be able to do
import { sort } from 'fp-ts/Array'
import { getMonoid } from 'fp-ts/Ord'
import { concatAll } from 'fp-ts/Monoid'
const M = getMonoid<string>
const byBothOrds = concatAll(M)([ordString, ordStringLength])
const sorted = sort(byBothOrds)(listOfStrings)
It should be fairly obvious how to add additional Ord
s to this, as you asked.