array-extended
Version:
Additional array extensions with a chainable api
669 lines (598 loc) • 20.9 kB
JavaScript
(function () {
"use strict";
/*global define*/
function defineArray(extended, is, args) {
var isString = is.isString,
isArray = Array.isArray || is.isArray,
isDate = is.isDate,
floor = Math.floor,
abs = Math.abs,
mathMax = Math.max,
mathMin = Math.min,
arrayProto = Array.prototype,
arrayIndexOf = arrayProto.indexOf,
arrayForEach = arrayProto.forEach,
arrayMap = arrayProto.map,
arrayReduce = arrayProto.reduce,
arrayReduceRight = arrayProto.reduceRight,
arrayFilter = arrayProto.filter,
arrayEvery = arrayProto.every,
arraySome = arrayProto.some,
argsToArray = args.argsToArray;
function cross(num, cros) {
return reduceRight(cros, function (a, b) {
if (!isArray(b)) {
b = [b];
}
b.unshift(num);
a.unshift(b);
return a;
}, []);
}
function permute(num, cross, length) {
var ret = [];
for (var i = 0; i < cross.length; i++) {
ret.push([num].concat(rotate(cross, i)).slice(0, length));
}
return ret;
}
function intersection(a, b) {
var ret = [], aOne, i = -1, l;
l = a.length;
while (++i < l) {
aOne = a[i];
if (indexOf(b, aOne) !== -1) {
ret.push(aOne);
}
}
return ret;
}
var _sort = (function () {
var isAll = function (arr, test) {
return every(arr, test);
};
var defaultCmp = function (a, b) {
return a - b;
};
var dateSort = function (a, b) {
return a.getTime() - b.getTime();
};
return function _sort(arr, property) {
var ret = [];
if (isArray(arr)) {
ret = arr.slice();
if (property) {
if (typeof property === "function") {
ret.sort(property);
} else {
ret.sort(function (a, b) {
var aProp = a[property], bProp = b[property];
if (isString(aProp) && isString(bProp)) {
return aProp > bProp ? 1 : aProp < bProp ? -1 : 0;
} else if (isDate(aProp) && isDate(bProp)) {
return aProp.getTime() - bProp.getTime();
} else {
return aProp - bProp;
}
});
}
} else {
if (isAll(ret, isString)) {
ret.sort();
} else if (isAll(ret, isDate)) {
ret.sort(dateSort);
} else {
ret.sort(defaultCmp);
}
}
}
return ret;
};
})();
function indexOf(arr, searchElement, from) {
var index = (from || 0) - 1,
length = arr.length;
while (++index < length) {
if (arr[index] === searchElement) {
return index;
}
}
return -1;
}
function lastIndexOf(arr, searchElement, from) {
if (!isArray(arr)) {
throw new TypeError();
}
var t = Object(arr);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = len;
if (arguments.length > 2) {
n = Number(arguments[2]);
if (n !== n) {
n = 0;
} else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
n = (n > 0 || -1) * floor(abs(n));
}
}
var k = n >= 0 ? mathMin(n, len - 1) : len - abs(n);
for (; k >= 0; k--) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
function filter(arr, iterator, scope) {
if (arr && arrayFilter && arrayFilter === arr.filter) {
return arr.filter(iterator, scope);
}
if (!isArray(arr) || typeof iterator !== "function") {
throw new TypeError();
}
var t = Object(arr);
var len = t.length >>> 0;
var res = [];
for (var i = 0; i < len; i++) {
if (i in t) {
var val = t[i]; // in case fun mutates this
if (iterator.call(scope, val, i, t)) {
res.push(val);
}
}
}
return res;
}
function forEach(arr, iterator, scope) {
if (!isArray(arr) || typeof iterator !== "function") {
throw new TypeError();
}
if (arr && arrayForEach && arrayForEach === arr.forEach) {
arr.forEach(iterator, scope);
return arr;
}
for (var i = 0, len = arr.length; i < len; ++i) {
iterator.call(scope || arr, arr[i], i, arr);
}
return arr;
}
function every(arr, iterator, scope) {
if (arr && arrayEvery && arrayEvery === arr.every) {
return arr.every(iterator, scope);
}
if (!isArray(arr) || typeof iterator !== "function") {
throw new TypeError();
}
var t = Object(arr);
var len = t.length >>> 0;
for (var i = 0; i < len; i++) {
if (i in t && !iterator.call(scope, t[i], i, t)) {
return false;
}
}
return true;
}
function some(arr, iterator, scope) {
if (arr && arraySome && arraySome === arr.some) {
return arr.some(iterator, scope);
}
if (!isArray(arr) || typeof iterator !== "function") {
throw new TypeError();
}
var t = Object(arr);
var len = t.length >>> 0;
for (var i = 0; i < len; i++) {
if (i in t && iterator.call(scope, t[i], i, t)) {
return true;
}
}
return false;
}
function map(arr, iterator, scope) {
if (arr && arrayMap && arrayMap === arr.map) {
return arr.map(iterator, scope);
}
if (!isArray(arr) || typeof iterator !== "function") {
throw new TypeError();
}
var t = Object(arr);
var len = t.length >>> 0;
var res = [];
for (var i = 0; i < len; i++) {
if (i in t) {
res.push(iterator.call(scope, t[i], i, t));
}
}
return res;
}
function reduce(arr, accumulator, curr) {
var initial = arguments.length > 2;
if (arr && arrayReduce && arrayReduce === arr.reduce) {
return initial ? arr.reduce(accumulator, curr) : arr.reduce(accumulator);
}
if (!isArray(arr) || typeof accumulator !== "function") {
throw new TypeError();
}
var i = 0, l = arr.length >> 0;
if (arguments.length < 3) {
if (l === 0) {
throw new TypeError("Array length is 0 and no second argument");
}
curr = arr[0];
i = 1; // start accumulating at the second element
} else {
curr = arguments[2];
}
while (i < l) {
if (i in arr) {
curr = accumulator.call(undefined, curr, arr[i], i, arr);
}
++i;
}
return curr;
}
function reduceRight(arr, accumulator, curr) {
var initial = arguments.length > 2;
if (arr && arrayReduceRight && arrayReduceRight === arr.reduceRight) {
return initial ? arr.reduceRight(accumulator, curr) : arr.reduceRight(accumulator);
}
if (!isArray(arr) || typeof accumulator !== "function") {
throw new TypeError();
}
var t = Object(arr);
var len = t.length >>> 0;
// no value to return if no initial value, empty array
if (len === 0 && arguments.length === 2) {
throw new TypeError();
}
var k = len - 1;
if (arguments.length >= 3) {
curr = arguments[2];
} else {
do {
if (k in arr) {
curr = arr[k--];
break;
}
}
while (true);
}
while (k >= 0) {
if (k in t) {
curr = accumulator.call(undefined, curr, t[k], k, t);
}
k--;
}
return curr;
}
function toArray(o) {
var ret = [];
if (o !== null) {
var args = argsToArray(arguments);
if (args.length === 1) {
if (isArray(o)) {
ret = o;
} else if (is.isHash(o)) {
for (var i in o) {
if (o.hasOwnProperty(i)) {
ret.push([i, o[i]]);
}
}
} else {
ret.push(o);
}
} else {
forEach(args, function (a) {
ret = ret.concat(toArray(a));
});
}
}
return ret;
}
function sum(array) {
array = array || [];
if (array.length) {
return reduce(array, function (a, b) {
return a + b;
});
} else {
return 0;
}
}
function avg(arr) {
arr = arr || [];
if (arr.length) {
var total = sum(arr);
if (is.isNumber(total)) {
return total / arr.length;
} else {
throw new Error("Cannot average an array of non numbers.");
}
} else {
return 0;
}
}
function sort(arr, cmp) {
return _sort(arr, cmp);
}
function min(arr, cmp) {
return _sort(arr, cmp)[0];
}
function max(arr, cmp) {
return _sort(arr, cmp)[arr.length - 1];
}
function difference(arr1) {
var ret = arr1, args = flatten(argsToArray(arguments, 1));
if (isArray(arr1)) {
ret = filter(arr1, function (a) {
return indexOf(args, a) === -1;
});
}
return ret;
}
function removeDuplicates(arr) {
var ret = [], i = -1, l, retLength = 0;
if (arr) {
l = arr.length;
while (++i < l) {
var item = arr[i];
if (indexOf(ret, item) === -1) {
ret[retLength++] = item;
}
}
}
return ret;
}
function unique(arr) {
return removeDuplicates(arr);
}
function rotate(arr, numberOfTimes) {
var ret = arr.slice();
if (typeof numberOfTimes !== "number") {
numberOfTimes = 1;
}
if (numberOfTimes && isArray(arr)) {
if (numberOfTimes > 0) {
ret.push(ret.shift());
numberOfTimes--;
} else {
ret.unshift(ret.pop());
numberOfTimes++;
}
return rotate(ret, numberOfTimes);
} else {
return ret;
}
}
function permutations(arr, length) {
var ret = [];
if (isArray(arr)) {
var copy = arr.slice(0);
if (typeof length !== "number") {
length = arr.length;
}
if (!length) {
ret = [
[]
];
} else if (length <= arr.length) {
ret = reduce(arr, function (a, b, i) {
var ret;
if (length > 1) {
ret = permute(b, rotate(copy, i).slice(1), length);
} else {
ret = [
[b]
];
}
return a.concat(ret);
}, []);
}
}
return ret;
}
function zip() {
var ret = [];
var arrs = argsToArray(arguments);
if (arrs.length > 1) {
var arr1 = arrs.shift();
if (isArray(arr1)) {
ret = reduce(arr1, function (a, b, i) {
var curr = [b];
for (var j = 0; j < arrs.length; j++) {
var currArr = arrs[j];
if (isArray(currArr) && !is.isUndefined(currArr[i])) {
curr.push(currArr[i]);
} else {
curr.push(null);
}
}
a.push(curr);
return a;
}, []);
}
}
return ret;
}
function transpose(arr) {
var ret = [];
if (isArray(arr) && arr.length) {
var last;
forEach(arr, function (a) {
if (isArray(a) && (!last || a.length === last.length)) {
forEach(a, function (b, i) {
if (!ret[i]) {
ret[i] = [];
}
ret[i].push(b);
});
last = a;
}
});
}
return ret;
}
function valuesAt(arr, indexes) {
var ret = [];
indexes = argsToArray(arguments);
arr = indexes.shift();
if (isArray(arr) && indexes.length) {
for (var i = 0, l = indexes.length; i < l; i++) {
ret.push(arr[indexes[i]] || null);
}
}
return ret;
}
function union() {
var ret = [];
var arrs = argsToArray(arguments);
if (arrs.length > 1) {
for (var i = 0, l = arrs.length; i < l; i++) {
ret = ret.concat(arrs[i]);
}
ret = removeDuplicates(ret);
}
return ret;
}
function intersect() {
var collect = [], sets, i = -1 , l;
if (arguments.length > 1) {
//assume we are intersections all the lists in the array
sets = argsToArray(arguments);
} else {
sets = arguments[0];
}
if (isArray(sets)) {
collect = sets[0];
i = 0;
l = sets.length;
while (++i < l) {
collect = intersection(collect, sets[i]);
}
}
return removeDuplicates(collect);
}
function powerSet(arr) {
var ret = [];
if (isArray(arr) && arr.length) {
ret = reduce(arr, function (a, b) {
var ret = map(a, function (c) {
return c.concat(b);
});
return a.concat(ret);
}, [
[]
]);
}
return ret;
}
function cartesian(a, b) {
var ret = [];
if (isArray(a) && isArray(b) && a.length && b.length) {
ret = cross(a[0], b).concat(cartesian(a.slice(1), b));
}
return ret;
}
function compact(arr) {
var ret = [];
if (isArray(arr) && arr.length) {
ret = filter(arr, function (item) {
return !is.isUndefinedOrNull(item);
});
}
return ret;
}
function multiply(arr, times) {
times = is.isNumber(times) ? times : 1;
if (!times) {
//make sure times is greater than zero if it is zero then dont multiply it
times = 1;
}
arr = toArray(arr || []);
var ret = [], i = 0;
while (++i <= times) {
ret = ret.concat(arr);
}
return ret;
}
function flatten(arr) {
var set;
var args = argsToArray(arguments);
if (args.length > 1) {
//assume we are intersections all the lists in the array
set = args;
} else {
set = toArray(arr);
}
return reduce(set, function (a, b) {
return a.concat(b);
}, []);
}
function pluck(arr, prop) {
prop = prop.split(".");
var result = arr.slice(0);
forEach(prop, function (prop) {
var exec = prop.match(/(\w+)\(\)$/);
result = map(result, function (item) {
return exec ? item[exec[1]]() : item[prop];
});
});
return result;
}
function invoke(arr, func, args) {
args = argsToArray(arguments, 2);
return map(arr, function (item) {
var exec = isString(func) ? item[func] : func;
return exec.apply(item, args);
});
}
var array = {
toArray: toArray,
sum: sum,
avg: avg,
sort: sort,
min: min,
max: max,
difference: difference,
removeDuplicates: removeDuplicates,
unique: unique,
rotate: rotate,
permutations: permutations,
zip: zip,
transpose: transpose,
valuesAt: valuesAt,
union: union,
intersect: intersect,
powerSet: powerSet,
cartesian: cartesian,
compact: compact,
multiply: multiply,
flatten: flatten,
pluck: pluck,
invoke: invoke,
forEach: forEach,
map: map,
filter: filter,
reduce: reduce,
reduceRight: reduceRight,
some: some,
every: every,
indexOf: indexOf,
lastIndexOf: lastIndexOf
};
return extended.define(isArray, array).expose(array);
}
if ("undefined" !== typeof exports) {
if ("undefined" !== typeof module && module.exports) {
module.exports = defineArray(require("extended"), require("is-extended"), require("arguments-extended"));
}
} else if ("function" === typeof define && define.amd) {
define(["extended", "is-extended", "arguments-extended"], function (extended, is, args) {
return defineArray(extended, is, args);
});
} else {
this.arrayExtended = defineArray(this.extended, this.isExtended, this.argumentsExtended);
}
}).call(this);