oxjs/source/Ox/js/Array.js

217 lines
No EOL
6.2 KiB
JavaScript

'use strict';
/*@
Ox.compact <f> Returns an array w/o <code>undefined</code> values
> Ox.compact([null,,1,,2,,3])
[1, 2, 3]
@*/
Ox.compact = function(arr) {
return arr.filter(function(val) {
return !Ox.isNull(val) && !Ox.isUndefined(val);
});
};
/*@
Ox.flatten <f> Flattens an array
> Ox.flatten([1, [2, [3], 2], 1])
[1, 2, 3, 2, 1]
@*/
Ox.flatten = function(arr) {
// fixme: can this work for objects too?
var ret = [];
arr.forEach(function(val) {
if (Ox.isArray(val)) {
Ox.flatten(val).forEach(function(val) {
ret.push(val);
});
} else {
ret.push(val);
}
});
return ret;
};
/*@
Ox.merge <f> Merges an array with one or more other arrays
> Ox.merge([1], [2, 3, 2], [1])
[1, 2, 3, 2, 1]
> Ox.merge(1, [2, 3, 2], 1)
[1, 2, 3, 2, 1]
@*/
Ox.merge = function(arr) {
arr = Ox.toArray(arr);
Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg) {
Ox.forEach(Ox.toArray(arg), function(val) {
arr.push(val);
});
});
return arr;
};
/*@
Ox.range <f> Python-style range
(stop) -> <[n]> range
Returns an array of integers from <code>0</code> (inclusive) to
<code>stop</code> (exclusive).
(start, stop) -> <[n]> range
Returns an array of integers from <code>start</code> (inclusive) to
<code>stop</code> (exclusive).
(start, stop, step) -> <[n]> range
Returns an array of numbers from <code>start</code> (inclusive) to
<code>stop</code> (exclusive), incrementing by <code>step</step>.
start <n> Start value
stop <n> Stop value
step <n> Step value
> Ox.range(3)
[0, 1, 2]
> Ox.range(1, 4)
[1, 2, 3]
> Ox.range(3, 0)
[3, 2, 1]
> Ox.range(1, 2, 0.5)
[1, 1.5]
> Ox.range(-1, -2, -0.5)
[-1, -1.5]
@*/
Ox.range = function() {
var args = Ox.makeArray(arguments),
arr = [];
args.push(function(i) {
arr.push(i);
});
Ox.loop.apply(null, args);
return arr;
};
(function() {
function getSortValues(arr, fn) {
var arr_ = fn ? arr.map(fn) : arr,
len, matches = {}, sort = {};
// find leading numbers
arr.forEach(function(val, i) {
var match = /^\d+/.exec(arr_[i]);
matches[val] = match ? match[0] : '';
});
// get length of longest leading number
len = Ox.max(Ox.map(matches, function(val) {
return val.length;
}));
// pad leading numbers, make lowercase,
// and remove leading non-word characters
arr.forEach(function(val, i) {
sort[val] = (
matches[val]
? arr_[i].toString().replace(
matches[val], Ox.pad(matches[val], len)
)
: arr_[i]
).toLowerCase().replace(/^\W+/, '');
});
return sort;
}
/*@
Ox.sort <f> Sorts an array, handling leading digits and ignoring capitalization
(arr) -> <a> Sorted array
(arr, fn) -> Sorted array
arr <a> Array
fn <f|u> Optional map function that returns the value for the array element
> Ox.sort(['"z"', '10', '9', 'B', 'a'])
['9', '10', 'a', 'B', '"z"']
> Ox.sort([{id: 0, name: '80 Days'}, {id: 1, name: '8 Women'}], function(v) {return v.name})
[{id: 1, name: '8 Women'}, {id: 0, name: '80 Days'}]
@*/
Ox.sort = function(arr, fn) {
var sort = getSortValues(fn ? arr.map(fn) : arr);
return arr.sort(function(a, b) {
a = fn ? fn(a) : a;
b = fn ? fn(b) : b;
var ret = 0;
if (sort[a] < sort[b]) {
ret = -1;
} else if (sort[a] > sort[b]) {
ret = 1;
}
return ret;
});
};
/*@
Ox.sortBy <f> Sorts an array of objects by given properties
(arr, by) -> <a> Sorted array
arr <[o]> Array of objects
by <[s]> Array of object keys (asc: 'foo' or '+foo', desc: '-foo')
fn <o> Optional functions, per key, that return the sort value
> Ox.sortBy([{x: 1, y: 1}, {x: 1, y: 2}, {x: 2, y: 2}], ['+x', '-y'])
[{x: 1, y: 2}, {x: 1, y: 1}, {x: 2, y: 2}]
> Ox.sortBy([{id: 0, name: '80 Days'}, {id: 1, name: '8 Women'}], ['name'])
[{id: 1, name: '8 Women'}, {id: 0, name: '80 Days'}]
@*/
Ox.sortBy = function(arr, by, fn) {
var length = by.length, values = {};
by = by.map(function(v) {
return {
key: v.replace(/^[\+\-]/g, ''),
operator: v[0] == '-' ? '-' : '+'
};
});
fn = fn || {};
by.map(function(v) {
return v.key;
}).forEach(function(key) {
values[key] = getSortValues(arr.map(function(v) {
return v[key];
}), fn[key]);
});
return arr.sort(function(a, b) {
var aValue, bValue, index = 0, key, ret = 0;
while (ret == 0 && index < length) {
key = by[index].key;
aValue = values[key][a[key]];
bValue = values[key][b[key]];
if (aValue < bValue) {
ret = by[index].operator == '+' ? -1 : 1;
} else if (aValue > bValue) {
ret = by[index].operator == '+' ? 1 : -1;
} else {
index++;
}
}
return ret;
});
};
}());
/*@
Ox.unique <f> Returns an array without duplicate values
> Ox.unique([1, 2, 3, 2, 1])
[1, 2, 3]
> Ox.unique([NaN, NaN])
[]
@*/
Ox.unique = function(arr) {
return Ox.filter(arr, function(val, i) {
return arr.indexOf(val) == i;
});
};
/*@
Ox.zip <f> Zips an array of arrays
> Ox.zip([[0, 1], [2, 3], [4, 5]])
[[0, 2, 4], [1, 3, 5]]
> Ox.zip([0, 1, 2], [3, 4, 5])
[[0, 3], [1, 4], [2, 5]]
@*/
Ox.zip = function() {
var args = arguments.length == 1 ? arguments[0] : Ox.makeArray(arguments),
arr = [];
args[0].forEach(function(v, i) {
arr[i] = [];
args.forEach(function(v) {
arr[i].push(v[i]);
});
});
return arr;
};