A comparator function ascending
accepts two arguments - a
and b
. It must return an integer comparing the two.
I have a list that I want to sort by name, so I wrote the following functions.
Is there a functional idiom I can use to combine these two functions, rather than having byName
take responsibility for composing the resulting function?
const ascending = (a, b) => a.localeCompare(b);
const byName = (i) => i.get('name');
const useTogether = (...fns) => ...; // is there an idiomatic function like this?
// usage
items.sort(useTogether(byName(ascending)));
e.: Ok now with a little effort you actually can have a functional component with generic props. You are stuck using 'modern' syntax though as it employs an assignment and arrow function which is of no use for your generic case: Let's rewrite the variable assignment as a good old function:
In the meantime, we welcome your thoughts on the conceptual framework outlined below. It is impossible for an organization to apply its generic principles if not in the operative context of specific applications; while, conversely, the latter are not effective unless embedded into the former.
We will follow the steps below to implement Generics in our framework: 1 Create a Generic Interface 2 Create a class to implement generic interface methods 3 M odification of the Endpoints class 4 Modification of Steps class 5 R un the tests
And this is why there is a need for routine code refactoring. Code refactoring is important if you want to avoid the dreaded code rot. Code rot results from duplicate code, myriad patches, bad classifications, and other programming discrepancies.
You're looking for contravariant functors
To appreciate them properly, let's start by examining the most basic sorting program
const compare = (a, b) =>
a.localeCompare (b)
const data =
[ 'Cindy'
, 'Alice'
, 'Darius'
, 'Bertrand'
]
data.sort (compare)
console.log (data)
// Alice, Bertrand, Cindy, Darius
Nothing special there. Let's make our first contravariant functor, Comparison
. The sort causes mutation, but it's just for demonstration anyway. Focus on contramap
const compare = (a, b) =>
a.localeCompare (b)
const Comparison = (f = compare) =>
({ contramap : g =>
Comparison ((a, b) => f (g (a), g (b)))
, sort : xs =>
xs.sort (f)
})
const data =
[ { name: 'Cindy' }
, { name: 'Alice' }
, { name: 'Darius' }
, { name: 'Bertrand' }
]
Comparison ()
.contramap (x => x.name)
.sort (data)
console.log (data)
// Alice, Bertrand, Cindy, Darius
Composition law holds
m.contramap(f).contramap(g) == m.contramap(compose(f,g))
const compare = (a, b) =>
a.localeCompare (b)
const Comparison = (f = compare) =>
({ contramap : g =>
Comparison ((a, b) => f (g (a), g (b)))
, sort : xs =>
xs.sort (f)
})
const data =
[ { name: 'Cindy' }
, { name: 'Alice' }
, { name: 'Darius' }
, { name: 'Bertrand' }
]
const compose = (f, g) =>
x => f (g (x))
Comparison ()
.contramap (compose (x => x.substr (1), x => x.name))
// equivalent to
// .contramap (x => x.substr (1)) // sort starting with second letter
// .contramap (x => x.name) // get name property
.sort (data)
console.log (data)
// sorted by second letter this time (A, E, I, L)
// Darius, Bertrand, Cindy, Alice
// ^ ^ ^ ^
Implementing the monoid interface gives you cool things like "multi-sort"
const Eq =
0
const Lt =
-1
const Gt =
1
const Ord =
{ empty: Eq
, concat: (a,b) =>
a === Eq ? b : a
}
const compare = (a, b) =>
a < b ? Lt
: a > b ? Gt
: Eq
const Comparison = (f = compare) =>
({ compare: f
, contramap : g =>
Comparison ((a, b) => f (g (a), g (b)))
, concat : m =>
Comparison ((a, b) =>
Ord.concat (f (a, b), m.compare (a, b)))
, sort : xs =>
xs.sort (f)
})
const data =
[ { name: 'Alicia', age: 10 }
, { name: 'Alice', age: 15 }
, { name: 'Alice', age: 10 }
, { name: 'Alice', age: 16 }
]
const sortByName =
Comparison ()
.contramap (x => x.name)
const sortByAge =
Comparison ()
.contramap (x => x.age)
sortByName
.concat (sortByAge)
.sort (data)
console.log ('sorted by (name,age)', data)
// Alice 10
// Alice 15
// Alice 16
// Alicia 10
sortByAge
.concat (sortByName)
.sort (data)
console.log ('sorted by (age,name)', data)
// Alice 10
// Alicia 10
// Alice 15
// Alice 16
Read the linked article for more useful information and an introduction to transducers
I'm not sure if it satisfies what you're looking for, but here is one possible formulation of your useTogether
(with a somewhat different signature) that does WORK. I'm not aware of a standard function with precisely that effect.
const ascending = (a, b) => a.localeCompare(b);
const byName = (i) => i['name'];
const useTogether = (selector, consumer) => (...fnArgs) => consumer(...fnArgs.map(selector));
var items = [{ name: "C" }, { name: "A" }, { name: "B" }];
console.log(
items.sort(useTogether(byName, ascending))
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With