757 lines
No EOL
21 KiB
JavaScript
757 lines
No EOL
21 KiB
JavaScript
'use strict';
|
|
|
|
/*@
|
|
Ox.avg <f> Returns the average of an array's values, or an object's properties
|
|
(collection) -> <n> Average value
|
|
collection <[n]|o> Array or object with numerical values
|
|
> Ox.avg([-1, 0, 1])
|
|
0
|
|
> Ox.avg({a: 1, b: 2, c: 3})
|
|
2
|
|
> Ox.avg('avg is 0.1')
|
|
0.1
|
|
@*/
|
|
|
|
Ox.avg = function(obj) {
|
|
return Ox.sum(obj) / Ox.len(obj);
|
|
};
|
|
|
|
/*@
|
|
Ox.contains <f> Tests if a collection contains a value
|
|
> Ox.contains(['foo', 'bar'], 'foo')
|
|
true
|
|
> Ox.contains({foo: 'bar'}, 'bar')
|
|
true
|
|
> Ox.contains({foo: 'bar'}, 'foo')
|
|
false
|
|
> Ox.contains("foobar", "bar")
|
|
true
|
|
@*/
|
|
Ox.contains = function(col, val) {
|
|
/*
|
|
// fixme: rename to Ox.has or Ox.isIn?
|
|
// then it'd become convenient for arrays
|
|
*/
|
|
return (Ox.isObject(col) ? Ox.values(col) : col).indexOf(val) > -1;
|
|
};
|
|
|
|
/*@
|
|
Ox.copy <f> Returns a (shallow or deep) copy of an object or array
|
|
> (function() { var a = ['v'], b = Ox.copy(a); a[0] = null; return b[0]; }())
|
|
'v'
|
|
> (function() { var a = {k: 'v'}, b = Ox.copy(a); a.k = null; return b.k; }())
|
|
'v'
|
|
> Ox.clone(0)
|
|
0
|
|
@*/
|
|
|
|
Ox.copy = Ox.clone = function(col, deep) {
|
|
// fixme: remove references to Ox.clone
|
|
// fixme: is there any use case for shallow copy?
|
|
var ret = Ox.isArray(col) ? [] : {};
|
|
if (deep) {
|
|
Ox.forEach(col, function(val, key) {
|
|
ret[key] = ['array', 'object'].indexOf(Ox.typeOf(val)) > -1
|
|
? Ox.clone(val, true) : val;
|
|
});
|
|
} else {
|
|
ret = Ox.isArray(col) ? col.slice()
|
|
: Ox.isObject(col) ? Ox.extend({}, col)
|
|
: col;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.count <f> Counts the occurences of values in a collection
|
|
> Ox.count(['f', 'o', 'o'])
|
|
{f: 1, o: 2}
|
|
> Ox.count({a: 'f', b: 'o', c: 'o'})
|
|
{f: 1, o: 2}
|
|
> Ox.count('foo')
|
|
{f: 1, o: 2}
|
|
@*/
|
|
Ox.count = function(arr) {
|
|
var obj = {};
|
|
Ox.forEach(arr, function(v) {
|
|
obj[v] = (obj[v] || 0) + 1;
|
|
});
|
|
return obj;
|
|
};
|
|
|
|
/*@
|
|
Ox.every <f> Tests if every element of a collection satisfies a given condition
|
|
Unlike <code>[].every()</code>, <code>Ox.every()</code> works for arrays,
|
|
objects and strings.
|
|
> Ox.every([0, 1, 2], function(v, i) { return i == v; })
|
|
true
|
|
> Ox.every({a: 1, b: 2, c: 3}, function(v) { return v == 1; })
|
|
false
|
|
> Ox.every("foo", function(v) { return v == 'f'; })
|
|
false
|
|
> Ox.every([true, true, true])
|
|
true
|
|
@*/
|
|
Ox.every = function(col, fn) {
|
|
return Ox.filter(Ox.values(col), fn || function(v) {
|
|
return v;
|
|
}).length == Ox.len(col);
|
|
};
|
|
|
|
/*@
|
|
Ox.filter <f> Filters a collection by a given condition
|
|
Unlike <code>[].filter()</code>, <code>Ox.filter()</code> works for arrays,
|
|
objects and strings.
|
|
> Ox.filter([2, 1, 0], function(v, i) { return v == i; })
|
|
[1]
|
|
> Ox.keys(Ox.filter({a: 'c', b: 'b', c: 'a'}, function(v, k) { return v == k; }))
|
|
['b']
|
|
> Ox.filter(' foo bar ', function(v) { return v != ' '; })
|
|
'foobar'
|
|
@*/
|
|
|
|
Ox.filter = function(col, fn) {
|
|
var type = Ox.typeOf(col),
|
|
ret = type == 'array' ? [] : type == 'object' ? {} : '';
|
|
Ox.forEach(col, function(v, k) {
|
|
if (fn(v, k)) {
|
|
if (type == 'array') {
|
|
ret.push(v);
|
|
} else if (type == 'object') {
|
|
ret[k] = v;
|
|
} else {
|
|
ret += v;
|
|
}
|
|
}
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.find <f> Returns array elements that match a string
|
|
Returns an array of two arrays, the first containing leading matches
|
|
(exact match first), the second containing non-leading matches
|
|
> Ox.find(['foo', 'bar', 'foobar', 'barfoo'], 'foo')
|
|
[['foo', 'foobar'], ['barfoo']]
|
|
@*/
|
|
// fixme: wouldn't it make more sense to return just one array?
|
|
Ox.find = function(arr, str) {
|
|
var ret = [[], []];
|
|
str = str.toLowerCase();
|
|
arr.map(function(v) {
|
|
return v.toLowerCase();
|
|
}).forEach(function(v, i) {
|
|
var index = v.indexOf(str);
|
|
index > -1 && ret[index == 0 ? 0 : 1][v == str ? 'unshift' : 'push'](arr[i]);
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.forEach <f> forEach loop
|
|
<code>Ox.forEach()</code> loops over arrays, objects and strings.
|
|
Returning <code>false</code> from the iterator function acts like a
|
|
<code>break</code> statement (unlike <code>[].forEach()</code>, like
|
|
<code>$.each()</code>). The arguments of the iterator function are
|
|
<code>(value, key)</code> (like <code>[].forEach()</code>, unlike
|
|
<code>$.each()</code>).
|
|
(collection, callback) <a|o|s> The collection
|
|
(collection, callback, includePrototype) <a|o|s> The collection
|
|
collection <a|o|s> An array, object or string
|
|
callback <f> Callback function
|
|
value <*> Value
|
|
key <n|s> Key
|
|
includePrototype <b|false> If true, include prototype properties
|
|
<script>
|
|
Ox.test.string = "";
|
|
Ox.forEach(["f", "o", "o"], function(v, i) { Ox.test.string += i; });
|
|
Ox.forEach({a: "f", b: "o", c: "o"}, function(v, k) { Ox.test.string += k; });
|
|
Ox.forEach("foo", function(v) { Ox.test.string += v; });
|
|
</script>
|
|
> Ox.test.string
|
|
"012abcfoo"
|
|
@*/
|
|
Ox.forEach = function(col, fn, includePrototype) {
|
|
var isObject = Ox.isObject(col), key;
|
|
// Safari will not loop through an arguments array
|
|
col = Ox.isArguments(col) ? Ox.makeArray(col) : col;
|
|
for (key in col) {
|
|
key = isObject ? key : parseInt(key);
|
|
// fixme: fn.call(context, obj[key], key, obj) may be more standard...
|
|
if ((
|
|
includePrototype || Object.hasOwnProperty.call(col, key)
|
|
) && fn(col[key], key) === false) {
|
|
break;
|
|
}
|
|
}
|
|
return col;
|
|
};
|
|
|
|
/*@
|
|
Ox.getObjectById <f> Returns an array element with a given id
|
|
> Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo")
|
|
{id: "foo", title: "Foo"}
|
|
@*/
|
|
// fixme: should this be getElementById() ?
|
|
Ox.getObjectById = function(arr, id) {
|
|
var ret = null;
|
|
Ox.forEach(arr, function(v) {
|
|
if (v.id == id) {
|
|
ret = v;
|
|
return false;
|
|
}
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.getPositionById <f> Returns the index of an array element with a given id
|
|
> Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo")
|
|
0
|
|
@*/
|
|
// fixme: this should be getIndexById()
|
|
Ox.getPositionById = function(arr, id) {
|
|
var ret = -1;
|
|
Ox.forEach(arr, function(v, i) {
|
|
if (v.id == id) {
|
|
ret = i;
|
|
return false;
|
|
}
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
// fixme: and what about getElementBy() and getIndexBy() ?
|
|
|
|
/*@
|
|
Ox.getset <f> Generic getter and setter function
|
|
See examples for details.
|
|
# Usage --------------------------------------------------------------------
|
|
Ox.getset(options, args=[]) -> <o> all options
|
|
Ox.getset(options, args=[key]) -> <*> options[key]
|
|
Ox.getset(options, args=[key, value], callback, context) -> <f|o> context
|
|
sets options[key] to value and calls fn(key, value)
|
|
if the key/value pair was added or modified
|
|
Ox.getset(options, args=[{key: value}], callback, context) -> <f|o> context
|
|
sets multiple options and calls fn(key, value)
|
|
for every key/value pair that was added or modified
|
|
# Arguments ----------------------------------------------------------------
|
|
options <obj> Options object (key/value pairs)
|
|
args <arr> The arguments "array" of the caller function
|
|
callback <fun> Callback function
|
|
The callback is called for every key/value pair that was added or
|
|
modified.
|
|
key <s> Key
|
|
value <*> Value
|
|
context <obj> The parent object of the caller function (for chaining)
|
|
# Examples -----------------------------------------------------------------
|
|
<script>
|
|
Ox.test.object = new function() {
|
|
var options = {},
|
|
setOption = function(key, value) {
|
|
// handle added or modified options
|
|
},
|
|
that = this;
|
|
that.options = function() {
|
|
return Ox.getset(options, arguments, setOption, that);
|
|
};
|
|
return that;
|
|
};
|
|
</script>
|
|
> Ox.test.object.options("key", "val").options("key")
|
|
"val"
|
|
> Ox.test.object.options({foo: "foo", bar: "bar"}).options()
|
|
{"key": "val", "foo": "foo", "bar": "bar"}
|
|
@*/
|
|
|
|
Ox.getset = function(obj, args, callback, context) {
|
|
var obj_ = Ox.clone(obj), ret;
|
|
if (args.length == 0) {
|
|
// []
|
|
ret = obj_;
|
|
} else if (args.length == 1 && !Ox.isObject(args[0])) {
|
|
// [key]
|
|
ret = Ox.clone(obj[args[0]]);
|
|
} else {
|
|
// [key, val] or [{key: val, ...}]
|
|
args = Ox.makeObject(args);
|
|
obj = Ox.extend(obj, args);
|
|
Ox.forEach(args, function(val, key) {
|
|
if (!obj_ || !Ox.isEqual(obj_[key], val)) {
|
|
callback && callback(key, val);
|
|
}
|
|
});
|
|
ret = context;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*@
|
|
Ox.isEmpty <f> Returns true if a collection is empty
|
|
> Ox.isEmpty([])
|
|
true
|
|
> Ox.isEmpty({})
|
|
true
|
|
> Ox.isEmpty('')
|
|
true
|
|
> Ox.isEmpty(function() {})
|
|
true
|
|
> Ox.isEmpty(function(a) {})
|
|
false
|
|
> Ox.isEmpty(null)
|
|
false
|
|
> Ox.isEmpty()
|
|
false
|
|
@*/
|
|
Ox.isEmpty = function(val) {
|
|
return Ox.len(val) == 0;
|
|
};
|
|
|
|
/*@
|
|
Ox.keys <f> Returns the keys of a collection
|
|
Unlike <code>Object.keys()</code>, <code>Ox.keys()</code> works for arrays,
|
|
objects and strings.
|
|
> Ox.keys([1, 2, 3])
|
|
[0, 1, 2]
|
|
> Ox.keys([1,,3])
|
|
[0, 2]
|
|
# fixme?
|
|
# > Ox.keys([,])
|
|
# [0]
|
|
> Ox.keys({a: 1, b: 2, c: 3})
|
|
['a', 'b', 'c']
|
|
> Ox.keys('abc')
|
|
[0, 1, 2]
|
|
@*/
|
|
|
|
// fixme: is this really needed? arrays... ok... but strings??
|
|
Ox.keys = function(obj) {
|
|
var keys = [];
|
|
Ox.forEach(obj, function(v, k) {
|
|
keys.push(k);
|
|
});
|
|
return keys.sort();
|
|
};
|
|
|
|
/*@
|
|
Ox.last <f> Gets or sets the last element of an array
|
|
Unlike <code>foobarbaz[foobarbaz.length - 1]</code>,
|
|
<code>Ox.last(foobarbaz)</code> is short.
|
|
<script>
|
|
Ox.test.array = [1, 2, 3];
|
|
</script>
|
|
> Ox.last(Ox.test.array)
|
|
3
|
|
> Ox.last(Ox.test.array, 4)
|
|
[1, 2, 4]
|
|
> Ox.test.array
|
|
[1, 2, 4]
|
|
@*/
|
|
Ox.last = function(arr, val) {
|
|
var ret;
|
|
if (arguments.length == 1) {
|
|
ret = arr[arr.length - 1];
|
|
} else {
|
|
arr[arr.length - 1] = val;
|
|
ret = arr;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.len <f> Returns the length of an array, function, object or string
|
|
Not to be confused with <code>Ox.length</code>, which is the
|
|
<code>length</code> property of the <code>Ox</code> function
|
|
(<code>1</code>).
|
|
> Ox.len([1, 2, 3])
|
|
3
|
|
> Ox.len([,])
|
|
1
|
|
> Ox.len({a: 1, b: 2, c: 3})
|
|
3
|
|
> Ox.len(function(a, b, c) {})
|
|
3
|
|
> Ox.len('abc')
|
|
3
|
|
@*/
|
|
Ox.len = function(col) {
|
|
var type = Ox.typeOf(col);
|
|
return ['array', 'function', 'string'].indexOf(type) > -1 ? col.length
|
|
: type == 'object' ? Ox.values(col).length : void 0;
|
|
};
|
|
|
|
/*@
|
|
Ox.loop <f> For-loop, functional-style
|
|
Returning <code>false</code> from the iterator function acts like a
|
|
<code>break</code> statement. Unlike a <code>for</code> loop,
|
|
<code>Ox.loop</code> doesn't leak its counter variable to the outer scope,
|
|
but returns it.
|
|
(stop, callback) -> <n> Next value
|
|
equivalent to <code>for (var i = 0; i < stop; i++)</code>
|
|
(start, stop, callback) -> <n> Next value
|
|
equivalent to <code>for (var i = start; i < stop; i++)</code> or,
|
|
if <code>start</code> is larger than <code>stop</code>,
|
|
<code>for (var i = start; i > stop; i--)</code>
|
|
(start, stop, step, callback) -> <n> Next value
|
|
equivalent to <code>for (var i = start; i < stop; i += step)</code> or,
|
|
if <code>step</code> is negative,
|
|
<code>for (var i = start; i > stop; i += step)</code>
|
|
start <n> Start value
|
|
stop <n> Stop value (exclusive)
|
|
step <n> Step value
|
|
callback <f> Iterator function
|
|
i <n> Counter value
|
|
> Ox.loop(10, function(i) { return i != 4; })
|
|
4
|
|
> Ox.loop(0, 3, 2, function() {})
|
|
4
|
|
@*/
|
|
Ox.loop = function() {
|
|
var len = arguments.length,
|
|
start = len > 2 ? arguments[0] : 0,
|
|
stop = arguments[len > 2 ? 1 : 0],
|
|
step = len == 4 ? arguments[2] : (start <= stop ? 1 : -1),
|
|
callback = arguments[len - 1],
|
|
i;
|
|
for (i = start; step > 0 ? i < stop : i > stop; i += step) {
|
|
if (callback(i) === false) {
|
|
break;
|
|
};
|
|
}
|
|
return i;
|
|
};
|
|
|
|
/*@
|
|
Ox.makeArray <f> Takes an array-like object and returns a true array
|
|
Alias for <code>Array.prototype.slice.call</code>
|
|
(value) -> <a> True array
|
|
value <*> Array-like object
|
|
> (function() { return Ox.makeArray(arguments); }("foo", "bar"))
|
|
["foo", "bar"]
|
|
> Ox.makeArray("foo")
|
|
["f", "o", "o"]
|
|
> Ox.makeArray({0: "f", 1: "o", 2: "o", length: 3})
|
|
["f", "o", "o"]
|
|
@*/
|
|
|
|
Ox.makeArray = /MSIE/.test(navigator.userAgent)
|
|
? function(col) {
|
|
var i, len, ret = [];
|
|
try {
|
|
ret = Array.prototype.slice.call(col);
|
|
} catch(e) {
|
|
// handle MSIE NodeLists
|
|
len = col.length;
|
|
for (i = 0; i < len; i++) {
|
|
ret[i] = col[i];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
: function(col) {
|
|
return Array.prototype.slice.call(col);
|
|
};
|
|
|
|
/*@
|
|
Ox.makeObject <f> Takes an array and returns an object
|
|
<code>Ox.makeObject</code> is a helper for functions with two alternative
|
|
signatures like <code>('key', 'val')</code> and <code>({key: 'val'})</code>.
|
|
> (function() { return Ox.makeObject(arguments); }({foo: 1, bar: 2}))
|
|
{foo: 1, bar: 2}
|
|
> (function() { return Ox.makeObject(arguments); }('foo', 1))
|
|
{foo: 1}
|
|
> (function() { return Ox.makeObject(arguments); }('foo'))
|
|
{foo: void 0}
|
|
> (function() { return Ox.makeObject(arguments); }())
|
|
{}
|
|
@*/
|
|
Ox.makeObject = function(obj) {
|
|
var ret = {};
|
|
if (Ox.isObject(obj[0])) {
|
|
// ({foo: 'bar'})
|
|
ret = obj[0];
|
|
} else if (obj.length) {
|
|
// ('foo', 'bar')
|
|
ret[obj[0]] = obj[1]
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.map <f> Transforms the values of an array, object or string
|
|
Unlike <code>[].map()</code>, <code>Ox.map()</code> works for arrays,
|
|
objects and strings. Returning <code>null</code> from the iterator
|
|
function will remove the element from the collection.
|
|
> Ox.map([0, 0, 0], function(v, i) { return v == i; })
|
|
[true, false, false]
|
|
> Ox.map({a: 'a', b: 'a', c: 'a'}, function(v, k) { return v == k; })
|
|
{a: true, b: false, c: false}
|
|
> Ox.map("000", function(v, i) { return v == i; })
|
|
[true, false, false]
|
|
> Ox.map([0, 1, 2, 4], function(v, i) { return v ? i == v : null; })
|
|
[true, true, false]
|
|
# fixme?
|
|
# > Ox.map([,], function(v, i) { return i; })
|
|
# [0]
|
|
@*/
|
|
|
|
// FIXME: it would sometimes be nice to have Ox.map(3, function(i) {...})
|
|
// instead of Ox.range(3).map(function(i) {...})
|
|
|
|
Ox.map = function(obj, fn) {
|
|
// fixme: return null to filter out may be a bit esoteric
|
|
var isObject = Ox.isObject(obj),
|
|
ret = isObject ? {} : [];
|
|
Ox.forEach(obj, function(val, key) {
|
|
var map;
|
|
if ((map = fn(val, key)) !== null) {
|
|
ret[isObject ? key : ret.length] = map;
|
|
}
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.max <f> Returns the maximum value of a collection
|
|
> Ox.max([1, 2, 3])
|
|
3
|
|
> Ox.max({a: 1, b: 2, c: 3})
|
|
3
|
|
> Ox.max('123')
|
|
3
|
|
@*/
|
|
Ox.max = function(col) {
|
|
return Math.max.apply(Math, Ox.values(col));
|
|
};
|
|
|
|
/*@
|
|
Ox.min <f> Returns the minimum value of a collection
|
|
> Ox.min([1, 2, 3])
|
|
1
|
|
> Ox.min({a: 1, b: 2, c: 3})
|
|
1
|
|
> Ox.min('123')
|
|
1
|
|
@*/
|
|
Ox.min = function(col) {
|
|
return Math.min.apply(Math, Ox.values(col));
|
|
};
|
|
|
|
/*@
|
|
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;
|
|
};
|
|
|
|
/*@
|
|
Ox.setPropertyOnce <f> Sets a property once
|
|
Given a array of objects, each of which has a property with a boolean
|
|
value, this sets exactly one of these to true, and returns the index
|
|
of the object whose property is true.
|
|
> Ox.setPropertyOnce([{selected: false}, {selected: false}], 'selected')
|
|
0
|
|
> Ox.setPropertyOnce([{selected: false}, {selected: true}], 'selected')
|
|
1
|
|
> Ox.setPropertyOnce([{selected: true}, {selected: true}], 'selected')
|
|
0
|
|
@*/
|
|
// fixme: strange name, and shouldn't it return the full array?
|
|
Ox.setPropertyOnce = function(arr, str) {
|
|
var pos = -1;
|
|
Ox.forEach(arr, function(v, i) {
|
|
if (pos == -1 && arr[i][str]) {
|
|
pos = i;
|
|
} else if (pos > -1 && arr[i][str]) {
|
|
delete arr[i][str];
|
|
}
|
|
});
|
|
if (pos == -1) {
|
|
arr[0][str] = true;
|
|
pos = 0;
|
|
}
|
|
return pos;
|
|
};
|
|
|
|
/*@
|
|
Ox.shuffle <f> Randomizes the order of values within a collection
|
|
> Ox.shuffle([1, 2, 3]).length
|
|
3
|
|
> Ox.len(Ox.shuffle({a: 1, b: 2, c: 3}))
|
|
3
|
|
> Ox.shuffle('123').length
|
|
3
|
|
@*/
|
|
|
|
Ox.shuffle = function(col) {
|
|
var keys, ret, type = Ox.typeOf(col), values;
|
|
function sort() {
|
|
return Math.random() - 0.5;
|
|
}
|
|
if (type == 'array') {
|
|
ret = col.sort(sort);
|
|
} else if (type == 'object') {
|
|
keys = Object.keys(col);
|
|
values = Ox.values(col).sort(sort);
|
|
ret = {};
|
|
keys.forEach(function(key, i) {
|
|
ret[key] = values[i]
|
|
});
|
|
} else if (type == 'string') {
|
|
ret = col.split('').sort(sort).join('');
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
/*@
|
|
Ox.some <f> Tests if one or more elements of a collection satisfy a given condition
|
|
Unlike <code>[].some()</code>, <code>Ox.some()</code> works for arrays,
|
|
objects and strings.
|
|
> Ox.some([2, 1, 0], function(i, v) { return i == v; })
|
|
true
|
|
> Ox.some({a: 1, b: 2, c: 3}, function(v) { return v == 1; })
|
|
true
|
|
> Ox.some("foo", function(v) { return v == 'f'; })
|
|
true
|
|
@*/
|
|
Ox.some = function(obj, fn) {
|
|
return Ox.filter(Ox.values(obj), fn).length > 0;
|
|
};
|
|
|
|
/*@
|
|
Ox.sub <f> Returns a substring or sub-array
|
|
Ox.sub behaves like collection[start:stop] in Python
|
|
(or, for strings, like str.substring() with negative values for stop)
|
|
> Ox.sub([1, 2, 3], 1, -1)
|
|
[2]
|
|
> Ox.sub('foobar', 1)
|
|
"oobar"
|
|
> Ox.sub('foobar', -1)
|
|
"r"
|
|
> Ox.sub('foobar', 1, 5)
|
|
"ooba"
|
|
> Ox.sub('foobar', 1, -1)
|
|
"ooba"
|
|
> Ox.sub('foobar', -5, 5)
|
|
"ooba"
|
|
> Ox.sub('foobar', -5, -1)
|
|
"ooba"
|
|
@*/
|
|
Ox.sub = function(col, start, stop) {
|
|
stop = Ox.isUndefined(stop) ? col.length : stop;
|
|
start = start < 0 ? col.length + start : start;
|
|
stop = stop < 0 ? col.length + stop : stop;
|
|
return Ox.isArray(col) ? Ox.filter(col, function(val, key) {
|
|
return key >= start && key < stop;
|
|
}) : col.substring(start, stop);
|
|
}
|
|
|
|
/*@
|
|
Ox.sum <f> Returns the sum of the values of a collection
|
|
> Ox.sum(1, 2, 3)
|
|
6
|
|
> Ox.sum([1, 2, 3])
|
|
6
|
|
> Ox.sum({a: 1, b: 2, c: 3})
|
|
6
|
|
> Ox.sum('123')
|
|
6
|
|
> Ox.sum('123foo')
|
|
6
|
|
> Ox.sum('08', -2, 'foo')
|
|
6
|
|
@*/
|
|
Ox.sum = function(col) {
|
|
var sum = 0;
|
|
col = arguments.length > 1 ? Ox.makeArray(arguments) : col;
|
|
Ox.forEach(col, function(val) {
|
|
val = +val;
|
|
sum += Ox.isNumber(val) ? val : 0;
|
|
});
|
|
return sum;
|
|
};
|
|
|
|
Ox.toArray = function(obj) {
|
|
// fixme: can this be thrown out?
|
|
/*
|
|
>>> Ox.toArray('foo')
|
|
['foo']
|
|
>>> Ox.toArray(['foo'])
|
|
['foo']
|
|
*/
|
|
var arr;
|
|
if (Ox.isArray(obj)) {
|
|
arr = obj;
|
|
} else if (Ox.isArguments(obj)) {
|
|
arr = Ox.makeArray(obj);
|
|
} else {
|
|
arr = [obj];
|
|
}
|
|
return arr;
|
|
};
|
|
|
|
/*@
|
|
Ox.values <f> Returns the values of a collection
|
|
> Ox.values([1, 2, 3])
|
|
[1, 2, 3]
|
|
> Ox.values({a: 1, b: 2, c: 3})
|
|
[1, 2, 3]
|
|
> Ox.values('abc')
|
|
['a', 'b', 'c']
|
|
> Ox.values([1,,3])
|
|
[1, 3]
|
|
@*/
|
|
Ox.values = function(col) {
|
|
// Ox.values(str) is identical to str.split('')
|
|
var values = [];
|
|
Ox.forEach(col, function(val) {
|
|
values.push(val);
|
|
});
|
|
return values;
|
|
};
|
|
|
|
/*@
|
|
Ox.walk <f> Recursively walk a tree-like key/value store
|
|
<script>
|
|
Ox.test.number = 0;
|
|
Ox.walk({a: 1, b: {c: 2, d: 3}}, function (v) {
|
|
Ox.test.number += Ox.isNumber(v) ? v : 0;
|
|
});
|
|
</script>
|
|
> Ox.test.number
|
|
6
|
|
@*/
|
|
Ox.walk = function(obj, fn) {
|
|
Ox.forEach(obj, function(val, key) {
|
|
fn(val, key, obj);
|
|
Ox.walk(obj[key], fn);
|
|
});
|
|
}; |