283 lines
6.5 KiB
JavaScript
283 lines
6.5 KiB
JavaScript
/**
|
|
* hof.js by github.com/zg
|
|
*/
|
|
|
|
/**
|
|
* _assert - a helper that throws an exception if condition is not met
|
|
* @param condition the condition to check
|
|
* @param the message to display (optional)
|
|
*/
|
|
_assert = function(condition, message) {
|
|
if (!condition) {
|
|
var message = message || "Assertion failed";
|
|
if (typeof Error !== "undefined") {
|
|
throw new Error(message);
|
|
}
|
|
throw message; // Fallback
|
|
}
|
|
}
|
|
|
|
/**
|
|
* _equals - a helper that checks if two objects are equal to each other
|
|
* @param a the first object
|
|
* @param b the second object
|
|
* @return true if the objects are equal, else false
|
|
*/
|
|
_equals = function(a, b) {
|
|
if (a === b) {
|
|
return true;
|
|
}
|
|
if (a == null || b == null) {
|
|
return false;
|
|
}
|
|
if (a.length != b.length) {
|
|
return false;
|
|
}
|
|
for (index in a) {
|
|
if (Object.prototype.toString.call(a[index]) == "[object Array]") {
|
|
if (!_equals(a[index], b[index])) {
|
|
return false;
|
|
}
|
|
} else if (a[index] !== b[index]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* average - compute the average of a given array of integers
|
|
* @param arr the array containing integers
|
|
* @return average of the values
|
|
*/
|
|
average = function(arr) {
|
|
return reduce(arr, function(a, b) {
|
|
return a + b;
|
|
}, 0) / arr.length;
|
|
}
|
|
|
|
/**
|
|
* every - checks if every element in arr has item
|
|
* @param item the item to check
|
|
* @return true if all items in arr are item, else false
|
|
*/
|
|
every = function(arr, item) {
|
|
for (index in arr) {
|
|
if (!_equals(arr[index], item)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* filter - return an array of items that meet a predicate
|
|
* @param arr the array
|
|
* @param pred the predicate
|
|
* @return a filtered array
|
|
*/
|
|
filter = function(arr, pred) {
|
|
var ret = [];
|
|
for (index in arr) {
|
|
if (pred(arr[index])) {
|
|
ret.push(arr[index]);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* fold - aggregates all elements of an array based on a function, starting
|
|
* with some initial value
|
|
* @param arr the array
|
|
* @param func the function
|
|
* @param init the initial value
|
|
* @return the aggregation of all elements
|
|
*/
|
|
fold = function(arr, func, init) {
|
|
var ret = init;
|
|
for (index in arr) {
|
|
ret = func(ret, arr[index]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* foldRight - aggregates all elements of an array, starting at the last
|
|
* index with an initial value, based on a function
|
|
* @param arr the array
|
|
* @param func the function
|
|
* @param init the initial value
|
|
* @return the aggregation of all elements
|
|
*/
|
|
foldRight = function(arr, func, init) {
|
|
var ret = init;
|
|
for (index in arr) {
|
|
ret = func(ret, arr[arr.length - index - 1]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* forEach - applies a function to each element of arr
|
|
* @param arr the array
|
|
* @param func the function to run
|
|
*/
|
|
forEach = function(arr, func) {
|
|
for (index in arr) {
|
|
func(arr[index]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* groupBy - group elements of an array based on a grouping function
|
|
* @param arr the array
|
|
* @param grouping the grouping function
|
|
* @return an array containing arrays of groupings
|
|
*/
|
|
groupBy = function(arr, grouping) {
|
|
var ret = [];
|
|
for (index in arr) {
|
|
group = grouping(arr[index]);
|
|
if (!ret[group]) {
|
|
ret[group] = [];
|
|
}
|
|
ret[group].push(arr[index]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* map - map an array of elements to values based on a function
|
|
* @param arr the array
|
|
* @param func the mapping function
|
|
* @return an array containing the mapping
|
|
*/
|
|
map = function(arr, func) {
|
|
var ret = [];
|
|
for (index in arr) {
|
|
ret.push(func(arr[index]));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* none - check if all elements in an array are not equal to item
|
|
* @param arr the array
|
|
* @param item the item to check
|
|
* @return true if none of the elements match, else false
|
|
*/
|
|
none = function(arr, item) {
|
|
for (index in arr) {
|
|
if (!_equals(arr[index], item)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* product - multiply all values of function from start to end
|
|
* @param func the function
|
|
* @param start the starting value
|
|
* @param end the ending value
|
|
* @return the product of all values between start and end
|
|
*/
|
|
product = function(start, end, func) {
|
|
var func = func || function(x) {
|
|
return x;
|
|
};
|
|
return fold(map(range(start, end + 1), func), function(a, b) {
|
|
return a * b;
|
|
}, 1);
|
|
}
|
|
|
|
/**
|
|
* range - build a range of numbers between start and end, exclusive
|
|
* @param start lower bound
|
|
* @param end upper bound
|
|
* @return an array of numbers between start and end, exclusive
|
|
*/
|
|
range = function(start, end, step) {
|
|
_assert(!(step === 0), "Cannot take a step");
|
|
var step = step || 1;
|
|
_assert(!(start < end && step < 0), "Invalid range (infinite loop)");
|
|
_assert(!(end < start && 0 < step), "Invalid range (infinite loop)");
|
|
var ret = [];
|
|
if (0 < step) {
|
|
for (ret.push(start); ret[ret.length - 1] + step < end; ret.push(ret[ret.length - 1] + step));
|
|
} else {
|
|
for (ret.push(start); ret[ret.length - 1] + step > end; ret.push(ret[ret.length - 1] + step));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* reduce - aggregates all elements of an array based on a function
|
|
* @param arr the array
|
|
* @param func the function
|
|
* @return the aggregation of all elements
|
|
*/
|
|
reduce = function(arr, func) {
|
|
return fold(arr, func, 0);
|
|
}
|
|
|
|
/**
|
|
* reduceRight - aggregates all elements of an array, starting at the last
|
|
* index, based on a function
|
|
* @param arr the array
|
|
* @param func the function
|
|
* @return the aggregation of all elements
|
|
*/
|
|
reduceRight = function(arr, func) {
|
|
return foldRight(arr, func, 0);
|
|
}
|
|
|
|
/**
|
|
* reverse - reverse elements in an array
|
|
* @param arr the array
|
|
* @return the reversed array
|
|
*/
|
|
reverse = function(arr) {
|
|
var half = arr.length >> 1;
|
|
for (index in arr) {
|
|
if (index < half) {
|
|
arr[arr.length - index - 1] ^= arr[index];
|
|
arr[index] ^= arr[arr.length - index - 1];
|
|
arr[arr.length - index - 1] ^= arr[index];
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
/**
|
|
* some - check if an array has some element in it
|
|
* @param arr the array
|
|
* @param item the item to check
|
|
* @return true if the array contains item, else false
|
|
*/
|
|
some = function(arr, item) {
|
|
for (index in arr) {
|
|
if (_equals(arr[index], item)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* sum - summation for all values of function from start to end
|
|
* @param func the function
|
|
* @param start the starting value
|
|
* @param end the ending value
|
|
* @return the summation of all values between start and end
|
|
*/
|
|
sum = function(start, end, func) {
|
|
var func = func || function(x) {
|
|
return x;
|
|
};
|
|
return reduce(map(range(start, end + 1), func), function(a, b) {
|
|
return a + b;
|
|
});
|
|
}
|