2011-11-05 16:46:53 +00:00
|
|
|
'use strict';
|
|
|
|
|
2011-10-07 01:04:47 +00:00
|
|
|
/*@
|
2012-03-29 10:11:45 +00:00
|
|
|
Ox.compact <f> Removes <code>null</code> or <code>undefined</code> values
|
2011-10-07 01:04:47 +00:00
|
|
|
> Ox.compact([null,,1,,2,,3])
|
|
|
|
[1, 2, 3]
|
|
|
|
@*/
|
|
|
|
Ox.compact = function(arr) {
|
2012-01-03 19:41:50 +00:00
|
|
|
return arr.filter(function(val) {
|
|
|
|
return !Ox.isNull(val) && !Ox.isUndefined(val);
|
2011-10-07 01:04:47 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/*@
|
2012-01-03 19:41:50 +00:00
|
|
|
Ox.flatten <f> Flattens an array
|
2011-10-07 01:04:47 +00:00
|
|
|
> 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
|
2012-03-29 10:11:45 +00:00
|
|
|
For convenience, literals are treated as arrays with one element
|
2011-10-07 01:04:47 +00:00
|
|
|
> 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) {
|
2012-01-03 19:41:50 +00:00
|
|
|
arr = Ox.toArray(arr);
|
2011-10-07 01:04:47 +00:00
|
|
|
Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg) {
|
2012-01-03 19:41:50 +00:00
|
|
|
Ox.forEach(Ox.toArray(arg), function(val) {
|
2011-10-07 01:04:47 +00:00
|
|
|
arr.push(val);
|
2012-01-03 19:41:50 +00:00
|
|
|
});
|
2011-10-07 01:04:47 +00:00
|
|
|
});
|
|
|
|
return arr;
|
|
|
|
};
|
|
|
|
|
2012-01-07 07:20:02 +00:00
|
|
|
/*@
|
|
|
|
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
|
2012-03-29 10:11:45 +00:00
|
|
|
<code>stop</code> (exclusive), incrementing by <code>step</code>.
|
2012-01-07 07:20:02 +00:00
|
|
|
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() {
|
2012-03-29 10:19:42 +00:00
|
|
|
var arr = [];
|
|
|
|
Ox.loop.apply(null, Ox.merge(Ox.makeArray(arguments), function(i) {
|
2012-01-07 07:20:02 +00:00
|
|
|
arr.push(i);
|
2012-03-29 10:19:42 +00:00
|
|
|
}));
|
2012-01-07 07:20:02 +00:00
|
|
|
return arr;
|
|
|
|
};
|
|
|
|
|
2012-01-11 10:47:06 +00:00
|
|
|
(function() {
|
|
|
|
|
2012-03-20 09:34:50 +00:00
|
|
|
function getSortValues(arr, fn) {
|
|
|
|
var arr_ = fn ? arr.map(fn) : arr,
|
|
|
|
len, matches = {}, sort = {};
|
2012-01-11 10:47:06 +00:00
|
|
|
// find leading numbers
|
2012-03-20 09:34:50 +00:00
|
|
|
arr.forEach(function(val, i) {
|
|
|
|
var match = /^\d+/.exec(arr_[i]);
|
2012-01-11 10:47:06 +00:00
|
|
|
matches[val] = match ? match[0] : '';
|
|
|
|
});
|
|
|
|
// get length of longest leading number
|
|
|
|
len = Ox.max(Ox.map(matches, function(val) {
|
|
|
|
return val.length;
|
|
|
|
}));
|
2012-03-20 09:34:50 +00:00
|
|
|
// pad leading numbers, make lowercase,
|
|
|
|
// and remove leading non-word characters
|
|
|
|
arr.forEach(function(val, i) {
|
2012-01-11 10:47:06 +00:00
|
|
|
sort[val] = (
|
|
|
|
matches[val]
|
2012-03-20 09:34:50 +00:00
|
|
|
? arr_[i].toString().replace(
|
|
|
|
matches[val], Ox.pad(matches[val], len)
|
|
|
|
)
|
|
|
|
: arr_[i]
|
2012-01-12 10:39:05 +00:00
|
|
|
).toLowerCase().replace(/^\W+/, '');
|
2012-01-11 10:47:06 +00:00
|
|
|
});
|
|
|
|
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
|
2012-01-12 10:39:05 +00:00
|
|
|
> Ox.sort(['"z"', '10', '9', 'B', 'a'])
|
|
|
|
['9', '10', 'a', 'B', '"z"']
|
2012-01-11 10:47:06 +00:00
|
|
|
> 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
|
2012-03-20 09:34:50 +00:00
|
|
|
by <[s]> Array of object keys (asc: 'foo' or '+foo', desc: '-foo')
|
|
|
|
fn <o> Optional functions, per key, that return the sort value
|
2012-01-11 10:47:06 +00:00
|
|
|
> 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'}]
|
|
|
|
@*/
|
2012-03-20 09:34:50 +00:00
|
|
|
Ox.sortBy = function(arr, by, fn) {
|
2012-01-11 10:47:06 +00:00
|
|
|
var length = by.length, values = {};
|
|
|
|
by = by.map(function(v) {
|
|
|
|
return {
|
|
|
|
key: v.replace(/^[\+\-]/g, ''),
|
|
|
|
operator: v[0] == '-' ? '-' : '+'
|
|
|
|
};
|
|
|
|
});
|
2012-03-20 09:34:50 +00:00
|
|
|
fn = fn || {};
|
2012-01-11 10:47:06 +00:00
|
|
|
by.map(function(v) {
|
2012-01-12 10:39:05 +00:00
|
|
|
return v.key;
|
2012-01-11 10:47:06 +00:00
|
|
|
}).forEach(function(key) {
|
|
|
|
values[key] = getSortValues(arr.map(function(v) {
|
|
|
|
return v[key];
|
2012-03-20 09:34:50 +00:00
|
|
|
}), fn[key]);
|
2012-01-11 10:47:06 +00:00
|
|
|
});
|
|
|
|
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;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
}());
|
2011-10-07 01:04:47 +00:00
|
|
|
|
|
|
|
/*@
|
|
|
|
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) {
|
2012-01-03 19:41:50 +00:00
|
|
|
return Ox.filter(arr, function(val, i) {
|
|
|
|
return arr.indexOf(val) == i;
|
2011-10-07 01:04:47 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/*@
|
|
|
|
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;
|
|
|
|
};
|