Is there any javascript library that contains functions like min_by,max_by in Ruby, which allow you pass a lambda as a criteria to compare. I found not such things in JQuery and underscore.js .
Chris's solution runs through the array three times; if the array is small, that's fine, but it's not ideal. You can use reduce
to get that down to a single pass (with a sort of as-you-go version of a Schwartzian transform):
// the only difference between minBy and maxBy is the ordering
// function, so abstract that out
Array.prototype.minBy = function(fn) {
return this.extremumBy(fn, Math.min);
};
Array.prototype.maxBy = function(fn) {
return this.extremumBy(fn, Math.max);
};
Array.prototype.extremumBy = function(pluck, extremum) {
return this.reduce(function(best, next) {
var pair = [ pluck(next), next ];
if (!best) {
return pair;
} else if (extremum.apply(null, [ best[0], pair[0] ]) == best[0]) {
return best;
} else {
return pair;
}
},null)[1];
}
Which works as advertised:
['albatross', 'dog', 'horse'].minBy( s => s.length )
// => "dog"
['albatross', 'dog', 'horse'].maxBy( s => s.length )
// => "albatross"
We could make it a little fancier by allowing the "plucker" to be an index or attribute name as well as a function:
const pluck = function(object, plucker) {
const t = typeof plucker;
switch (t) {
case 'function':
return plucker(object)
case 'number':
case 'string':
return object[plucker]
default:
throw new TypeError(`Invalid type for pluck: ${t}`)
}
}
Array.prototype.extremumBy = function(plucker, extremum) {
return this.reduce(function(best, next) {
var pair = [ pluck(next, plucker), next ];
...
And then this works, too:
['albatross', 'dog', 'horse'].minBy('length')
//=> 'dog'
['albatross', 'dog', 'horse'].maxBy('length')
//=> 'albatross'
I use reduce
var items = [{value: 0}];
var min = items.reduce((seed,item) => { return (seed && seed.value < item.value) ? seed : item; },null);
var max = items.reduce((seed,item) => { return (seed && seed.value > item.value) ? seed : item; },null);
Edit: Can also be added to prototype
Array.prototype.minBy = function(pluck) {
return this.reduce((min, x) => min && pluck(min) <= pluck(x) ? min : x, null)
}
Array.prototype.maxBy = function(pluck) {
return this.reduce((max, x) => max && pluck(max) >= pluck(x) ? max : x, null)
}
The example of min_by
in the documentation has the following example:
a = %w(albatross dog horse)
a.min_by {|x| x.length } #=> "dog"
So we can roughly translate that to JavaScript as:
var arr = ['albatross', 'dog', 'horse'];
function minBy(arr) {
var result = arr.map(function (el) { return el.length; });
var min = Math.min.apply(null, result);
return arr[result.indexOf(min)];
}
minBy(arr); // dog
max_by
would use Math.max.apply
instead.
function maxBy(arr) {
var result = arr.map(function (el) {
return el.length;
});
var min = Math.max.apply(null, result);
return arr[result.indexOf(min)];
}
maxBy(arr); // albatross
You could also amend the array prototype to get it more Rubyish.
if (!('minBy' in Array.prototype)) {
Array.prototype.minBy = function (type) {
var result = this.map(function (el) { return el[type]; });
var min = Math.min.apply(null, result);
return arr[result.indexOf(min)];
};
}
arr.minBy('length'); // dog
DEMO
To use this in the same way as Ruby, ie call it on the array:
Array.prototype.minBy = function(lambda) {
var lambdaFn;
if (typeof(lambda) === "function") {
lambdaFn = lambda;
} else {
lambdaFn = function(x){
return x[lambda];
}
}
var mapped = this.map(lambdaFn);
var minValue = Math.min.apply(Math, mapped);
return this[mapped.indexOf(minValue)];
}
So the ruby example becomes:
['albatross', 'dog', 'horse'].minBy(function(x){return x.length }) // = 'dog'
or:
['albatross', 'dog', 'horse'].minBy("length") // = 'dog'
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