A very common situation I run into, and see in others code is finding the 'best match' (for lack of a better term) in an array/collection. Common examples would be finding the min/max item, or finding the closest item to a specified value. When dealing with a number, Math.min/Math.max works fine, but that only accounts for a small portion of use cases, and still requires breaking out of the array method chain, and passing the array into a min()/max() method.
This is generally implemented in one of two ways.
-
using
array.sort
and then grabbing the head/tail (least performant by far, but most readable, so still common) -
Using a .reduce, and keeping track of the current 'winner' as you iterate through the array.
Some examples using reduce:
const dates = [
new Date(12345),
new Date(123456),
new Date(1234567)
]
const compFn = (a: Date, b: Date) => a < b;
const lowestDate = dates.reduce<Date | undefined>( (acc, n) => {
if(typeof acc === "undefined"){
return n;
}
return compFn(acc, n)
? n
: acc;
}, undefined)
/*
* with .winner, this could be written as
* const lowestDate = array.winner ( (a, b) => a > b );
*/
Here's an example with a class comparison
class Person {
name: string;
constructor(name: string){
this.name = name;
}
getName() {
return this.name;
}
}
const people = [
new Person("Bud"),
new Person("John"),
new Person("Sally")
];
const personCompFn = (a: Person, b: Person) =>
a.getName().length > b.getName().length;
const personWithLongestName = people.reduce<Person | undefined>( (acc, n) => {
return typeof acc === "undefined" || personCompFn(acc, n)
? n
: acc;
}, undefined)
/**
* with .winner, could be written as
* const winner = people.winner( (a, b) => a.getName().length > b.getName().length)
*/
The .winner
function would take a comparison callback very much like .sort, but would simply return true/false if a
is "better" than b
. Like so: const highest = arr.winner( (a, b) => a > b))