make Ox.forEach(object...) return a number; remove Ox.setPropertyOnce; move Ox.last to Array.js; update documentation and tests

This commit is contained in:
rolux 2012-06-02 16:08:01 +02:00
parent 686df92efa
commit ecd84770dd
4 changed files with 104 additions and 90 deletions

View file

@ -10,6 +10,8 @@ Ox.OptionGroup <f> OptionGroup
property <s|'checked'> property to check property <s|'checked'> property to check
@*/ @*/
// FIXME: Should be moved to Ox.js
Ox.OptionGroup = function(items, min, max, property) { Ox.OptionGroup = function(items, min, max, property) {
var length = items.length; var length = items.length;

View file

@ -457,6 +457,33 @@ Ox.indexOf = function(arr) {
}; };
*/ */
/*@
Ox.last <f> Gets or sets the last element of an array
Unlike `arrayWithALongName[arrayWithALongName.length - 1]`,
`Ox.last(arrayWithALongName)` 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('123')
'3'
@*/
Ox.last = function(array, value) {
var ret;
if (arguments.length == 1) {
ret = array[array.length - 1];
} else {
array[array.length - 1] = value;
ret = array;
}
return ret;
};
/*@ /*@
Ox.makeArray <f> Wraps any non-array in an array. Ox.makeArray <f> Wraps any non-array in an array.
(value) -> <a> Array (value) -> <a> Array

View file

@ -31,8 +31,16 @@
}; };
/*@ /*@
Ox.nonblockingForEach <f> Non-blocking forEach with synchronous iterator Ox.nonblockingForEach <f> Non-blocking `forEach` with synchronous iterator
(col, iterator[, that], callback[, ms]) -> <u> undefined (col, iterator[, that], callback[, ms]) -> <u> undefined
collection <a|o|s> Collection
iterator <f> Iterator function
value <*> Value
key <n|s> Key
collection <a|o|s> The collection
that <o> The iterator's `this` binding
callback <f> Callback function
ms <n> Number of milliseconds after which to insert a `setTimeout` call
@*/ @*/
Ox.nonblockingForEach = function(collection, iterator, that, callback, ms) { Ox.nonblockingForEach = function(collection, iterator, that, callback, ms) {
var i = 0, keys, last = Ox.last(arguments), var i = 0, keys, last = Ox.last(arguments),
@ -76,12 +84,13 @@
}; };
/*@ /*@
Ox.nonblockingMap <f> Non-blocking map with synchronous iterator Ox.nonblockingMap <f> Non-blocking `map` with synchronous iterator
(collection, iterator[, that]) -> <u> undefined (collection, iterator[, that], callback[, ms]) -> <u> undefined
collection <a|o|s> Collection collection <a|o|s> Collection
iterator <f> Iterator function iterator <f> Iterator function
that <o> The iterator's this binding that <o> The iterator's `this` binding
callback <f> Callback function callback <f> Callback function
ms <n> Number of milliseconds after which to insert a `setTimeout` call
<script> <script>
var time = +new Date(); var time = +new Date();
Ox.nonblockingMap( Ox.nonblockingMap(
@ -114,7 +123,7 @@
}; };
/*@ /*@
Ox.parallelForEach <f> forEach with asynchronous iterator, running in parallel Ox.parallelForEach <f> `forEach` with asynchronous iterator, running in parallel
(collection, iterator[, that], callback) -> <u> undefined (collection, iterator[, that], callback) -> <u> undefined
collection <a|o|s> Collection collection <a|o|s> Collection
iterator <f> Iterator function iterator <f> Iterator function
@ -140,7 +149,7 @@
}; };
/*@ /*@
Ox.parallelMap <f> Parallel map with asynchronous iterator Ox.parallelMap <f> Parallel `map` with asynchronous iterator
(collection, iterator[, that], callback) -> <u> undefined (collection, iterator[, that], callback) -> <u> undefined
collection <a|o|s> Collection collection <a|o|s> Collection
iterator <f> Iterator function iterator <f> Iterator function
@ -173,7 +182,7 @@
}; };
/*@ /*@
Ox.serialForEach <f> forEach with asynchronous iterator, run serially Ox.serialForEach <f> `forEach` with asynchronous iterator, run serially
(collection, iterator[, that], callback) -> <u> undefined (collection, iterator[, that], callback) -> <u> undefined
collection <a|o|s> Collection collection <a|o|s> Collection
iterator <f> Iterator function iterator <f> Iterator function
@ -208,7 +217,7 @@
}; };
/*@ /*@
Ox.serialMap <f> Serial map with asynchronous iterator Ox.serialMap <f> Serial `map` with asynchronous iterator
(collection, iterator[, that], callback) -> <u> undefined (collection, iterator[, that], callback) -> <u> undefined
collection <a|o|s> Collection collection <a|o|s> Collection
iterator <f> Iterator function iterator <f> Iterator function

View file

@ -16,13 +16,17 @@ Ox.avg = function(collection) {
}; };
/*@ /*@
Ox.clone <f> Returns a (shallow or deep) copy of an object or array Ox.clone <f> Returns a (shallow or deep) copy of an array or object
> (function() { var a = ['v'], b = Ox.clone(a); a[0] = null; return b[0]; }()) > (function() { var a = ['v'], b = Ox.clone(a); a[0] = null; return b[0]; }())
'v' 'v'
> (function() { var a = {k: 'v'}, b = Ox.clone(a); a.k = null; return b.k; }()) > (function() { var a = {k: 'v'}, b = Ox.clone(a); a.k = null; return b.k; }())
'v' 'v'
> Ox.clone(0) > Ox.clone(0)
0 0
> (function() { var a = [[0, 1]], b = Ox.clone(a); a[0][0] = null; return b[0]; }())
[null, 1]
> (function() { var a = [[0, 1]], b = Ox.clone(a, true); a[0][0] = null; return b[0]; }())
[0, 1]
@*/ @*/
Ox.clone = Ox.copy = function(collection, deep) { Ox.clone = Ox.copy = function(collection, deep) {
// fixme: copy or clone? // fixme: copy or clone?
@ -44,13 +48,16 @@ Ox.clone = Ox.copy = function(collection, deep) {
/*@ /*@
Ox.contains <f> Tests if a collection contains a value Ox.contains <f> Tests if a collection contains a value
(collection, value) -> <b> If true, the collection contains the value
collection <a|o|s> Collection
value <*> Any value
> Ox.contains(['foo', 'bar'], 'foo') > Ox.contains(['foo', 'bar'], 'foo')
true true
> Ox.contains({foo: 'bar'}, 'bar') > Ox.contains({foo: 'bar'}, 'bar')
true true
> Ox.contains({foo: 'bar'}, 'foo') > Ox.contains({foo: 'bar'}, 'foo')
false false
> Ox.contains("foobar", "bar") > Ox.contains('foobar', 'bar')
true true
@*/ @*/
// FIXME: a shorter name would be nice (but IE8 doesn't like 'in') // FIXME: a shorter name would be nice (but IE8 doesn't like 'in')
@ -62,6 +69,10 @@ Ox.contains = function(collection, value) {
/*@ /*@
Ox.count <f> Counts the occurences of values in a collection Ox.count <f> Counts the occurences of values in a collection
(collection) -> <o> Number of occurrences per value
(collection, value) -> <n> Number of occurrences of the given value
collection <a|o|s> Collection
value <*> Any value
> Ox.count(['f', 'o', 'o']) > Ox.count(['f', 'o', 'o'])
{f: 1, o: 2} {f: 1, o: 2}
> Ox.count({a: 'f', b: 'o', c: 'o'}) > Ox.count({a: 'f', b: 'o', c: 'o'})
@ -83,7 +94,14 @@ Ox.count = function(collection, value) {
/*@ /*@
Ox.every <f> Tests if every element of a collection satisfies a given condition Ox.every <f> Tests if every element of a collection satisfies a given condition
Unlike `[].every()`, `Ox.every()` works for arrays, objects and strings. Unlike `Array.prototype.every`, `Ox.every` works for arrays, objects and
strings.
(collection, iterator) -> <b> True if every element passes the test
collection <a|o|s> Collection
iterator <f> Iterator
value <*> Value
key <n|s> Index or key
collection <a|o|s> The collection
> Ox.every([0, 1, 2], function(v, i) { return v == i; }) > Ox.every([0, 1, 2], function(v, i) { return v == i; })
true true
> Ox.every({a: 1, b: 2, c: 3}, function(v) { return v == 1; }) > Ox.every({a: 1, b: 2, c: 3}, function(v) { return v == 1; })
@ -101,7 +119,8 @@ Ox.every = function(collection, iterator) {
/*@ /*@
Ox.filter <f> Filters a collection by a given condition Ox.filter <f> Filters a collection by a given condition
Unlike `[].filter()`, `Ox.filter()` works for arrays, objects and strings. Unlike `Array.prototype.filter`, `Ox.filter` works for arrays, objects and
strings.
> Ox.filter([2, 1, 0], function(v, i) { return v == i; }) > Ox.filter([2, 1, 0], function(v, i) { return v == i; })
[1] [1]
> Ox.filter({a: 'c', b: 'b', c: 'a'}, function(v, k) { return v == k; }) > Ox.filter({a: 'c', b: 'b', c: 'a'}, function(v, k) { return v == k; })
@ -128,31 +147,32 @@ Ox.filter = function(collection, iterator, that) {
return ret; return ret;
}; };
// FIXME: documentation is outdated!
/*@ /*@
Ox.forEach <f> forEach loop Ox.forEach <f> forEach loop
`Ox.forEach()` loops over arrays, objects and strings. Returning `false` `Ox.forEach` loops over arrays, objects and strings. Calling `Ox.Break`
from the iterator function acts like a `break` statement (unlike inside the iterator or returning `false` from the iterator acts like a
`[].forEach()`, like `$.each()`). The arguments of the iterator function are `break` statement. Unlike `Array.prototype.forEach`, which leaks its counter
`(value, key, index)` (more like `[].forEach()` than like `$.each()`). variable to the outer scope, `Ox.forEach` returns it.
(collection, callback) <a|o|s> The collection (collection, iterator[, that]) -> <n> Next index
(collection, callback, includePrototype) <a|o|s> The collection collection <a|o|s> Collection
collection <a|o|s> An array, object or string iterator <f> Iterator
callback <f> Iterator function
value <*> Value value <*> Value
key <n|s> Key key <n|s> Index or key
includePrototype <b|false> If true, include prototype properties collection <a|o|s> The collection
that <o> The iterator's `this` binding
<script> <script>
Ox.test.string = ""; Ox.test.string = "";
Ox.forEach(["f", "o", "o"], function(v, i) { Ox.test.string += i; }); 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({a: 'f', b: 'o', c: 'o'}, function(v, k) { Ox.test.string += k; });
Ox.forEach("foo", function(v) { Ox.test.string += v; }); Ox.forEach("foo", function(v) { Ox.test.string += v; });
</script> </script>
> Ox.test.string > Ox.test.string
"012abcfoo" "012abcfoo"
> Ox.forEach({a: 'f', b: 'o', c: 'o'}, function(v, k) { return v != 'o' });
1
@*/ @*/
Ox.forEach = function(collection, iterator, that) { Ox.forEach = function(collection, iterator, that) {
var i, key, type = Ox.typeOf(collection); var i = 0, key, type = Ox.typeOf(collection);
if (type != 'array' && type != 'object') { if (type != 'array' && type != 'object') {
collection = Ox.toArray(collection); collection = Ox.toArray(collection);
} }
@ -166,6 +186,7 @@ Ox.forEach = function(collection, iterator, that) {
break; break;
} }
} }
i++;
} }
} else { } else {
for (i = 0; i < collection.length; i++) { for (i = 0; i < collection.length; i++) {
@ -183,7 +204,7 @@ Ox.forEach = function(collection, iterator, that) {
throw error; throw error;
} }
} }
return type == 'object' ? key : i; return i;
}; };
/*@ /*@
@ -201,7 +222,8 @@ Ox.indexOf = function(collection, test) {
var index = Ox.forEach(collection, function(value) { var index = Ox.forEach(collection, function(value) {
test(value) && Ox.Break(); test(value) && Ox.Break();
}); });
return index == collection.length ? -1 : index; return Ox.isObject(collection) ? Object.keys(collection)[index] || null
: index == collection.length ? -1 : index;
}; };
/*@ /*@
@ -248,33 +270,6 @@ Ox.isEmpty = function(value) {
return Ox.len(value) === 0; return Ox.len(value) === 0;
}; };
/*@
Ox.last <f> Gets or sets the last element of an array
Unlike `arrayWithALongName[arrayWithALongName.length - 1]`,
`Ox.last(arrayWithALongName)` 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('123')
'3'
@*/
Ox.last = function(array, value) {
var ret;
if (arguments.length == 1) {
ret = array[array.length - 1];
} else {
array[array.length - 1] = value;
ret = array;
}
return ret;
};
/*@ /*@
Ox.len <f> Returns the length of an array, node list, object or string Ox.len <f> Returns the length of an array, node list, object or string
Not to be confused with `Ox.length`, which is the `length` property of the Not to be confused with `Ox.length`, which is the `length` property of the
@ -310,7 +305,8 @@ Ox.len = function(collection) {
/*@ /*@
Ox.map <f> Transforms the values of an array, object or string Ox.map <f> Transforms the values of an array, object or string
Unlike `[].map()`, `Ox.map()` works for arrays, objects and strings. Unlike `Array.prototype.map`, `Ox.map` works for arrays, objects and
strings.
> Ox.map([2, 1, 0], function(v, i) { return v == i; }) > Ox.map([2, 1, 0], function(v, i) { return v == i; })
[false, true, false] [false, true, false]
> Ox.map({a: 'b', b: 'b', c: 'b'}, function(v, k) { return v == k; }) > Ox.map({a: 'b', b: 'b', c: 'b'}, function(v, k) { return v == k; })
@ -391,36 +387,6 @@ Ox.reverse = function(collection) {
: collection.toString().split('').reverse().join(''); : collection.toString().split('').reverse().join('');
}; };
/*@
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?
// fixme: unused, there is OptionGroup (which should be part of Ox.js)
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 <f> Randomizes the order of values within a collection
> Ox.shuffle([1, 2, 3]).length > Ox.shuffle([1, 2, 3]).length
@ -484,7 +450,8 @@ if (
/*@ /*@
Ox.some <f> Tests if one or more elements of a collection meet a given condition Ox.some <f> Tests if one or more elements of a collection meet a given condition
Unlike `[].some()`, `Ox.some()` works for arrays, objects and strings. Unlike `Array.prototype.some`, `Ox.some` works for arrays, objects and
strings.
> Ox.some([2, 1, 0], function(i, v) { return i == v; }) > Ox.some([2, 1, 0], function(i, v) { return i == v; })
true true
> Ox.some({a: 1, b: 2, c: 3}, function(v) { return v == 1; }) > Ox.some({a: 1, b: 2, c: 3}, function(v) { return v == 1; })
@ -545,6 +512,8 @@ Ox.objectToArray = function(object, key) {
/*@ /*@
Ox.values <f> Returns the values of a collection Ox.values <f> Returns the values of a collection
(collection) -> <a> Array of values
collection <a|o|s> Collection
> Ox.values([1, 2, 3]) > Ox.values([1, 2, 3])
[1, 2, 3] [1, 2, 3]
> Ox.values({a: 1, b: 2, c: 3}) > Ox.values({a: 1, b: 2, c: 3})
@ -571,6 +540,13 @@ Ox.values = function(collection) {
/*@ /*@
Ox.walk <f> Iterates over a nested data structure Ox.walk <f> Iterates over a nested data structure
(collection, iterator[, that]) -> <u> undefined
collection <a|o|s> Collection
iterator <f> Iterator
value <*> Value
keys <a> Array of keys
collection <a|o|s> The collection
that <o> The iterator's `this` binding
<script> <script>
Ox.test.number = 0; Ox.test.number = 0;
Ox.walk({a: 1, b: {c: 2, d: 3}}, function(value) { Ox.walk({a: 1, b: {c: 2, d: 3}}, function(value) {
@ -586,11 +562,11 @@ Ox.walk <f> Iterates over a nested data structure
> Ox.test.array > Ox.test.array
[['a'], ['b', 'c'], ['b', 'd']] [['a'], ['b', 'c'], ['b', 'd']]
@*/ @*/
Ox.walk = function(collection, iterator, keys) { Ox.walk = function(collection, iterator, that, keys) {
keys = keys || []; keys = keys || [];
Ox.forEach(collection, function(value, key) { Ox.forEach(collection, function(value, key) {
var keys_ = keys.concat(key); var keys_ = keys.concat(key);
iterator(value, keys_, collection); iterator.call(that, value, keys_, collection);
Ox.walk(collection[key], iterator, keys_); Ox.walk(collection[key], iterator, that, keys_);
}); });
}; };