From 7f7a5aa44c9379ede0fa5e506d6fdfe8832707b1 Mon Sep 17 00:00:00 2001 From: rolux Date: Fri, 25 May 2012 09:39:33 +0200 Subject: [PATCH] in Ox.walk, pass array of keys to iterator; rename vars --- source/Ox/js/Collection.js | 448 ++++++++++++------------------------- 1 file changed, 145 insertions(+), 303 deletions(-) diff --git a/source/Ox/js/Collection.js b/source/Ox/js/Collection.js index ba92735e..b97fe5b7 100644 --- a/source/Ox/js/Collection.js +++ b/source/Ox/js/Collection.js @@ -11,8 +11,8 @@ Ox.avg Returns the average of an array's values, or an object's properties > Ox.avg('avg is 0.1') 0.1 @*/ -Ox.avg = function(obj) { - return Ox.sum(obj) / Ox.len(obj); +Ox.avg = function(collection) { + return Ox.sum(collection) / Ox.len(collection); }; /*@ @@ -24,18 +24,20 @@ Ox.clone Returns a (shallow or deep) copy of an object or array > Ox.clone(0) 0 @*/ -Ox.clone = Ox.copy = function(col, deep) { +Ox.clone = Ox.copy = function(collection, deep) { // fixme: copy or clone? - var ret = Ox.isArray(col) ? [] : {}; + var ret, type = Ox.typeOf(collection); if (deep) { - Ox.forEach(col, function(val, key) { - ret[key] = ['array', 'object'].indexOf(Ox.typeOf(val)) > -1 - ? Ox.clone(val, true) : val; + ret = type == 'array' ? [] : {}; + Ox.forEach(collection, function(value, key) { + type = Ox.typeOf(value); + ret[key] = type == 'array' || type == 'object' + ? Ox.clone(value, true) : value; }); } else { - ret = Ox.isArray(col) ? col.slice() - : Ox.isObject(col) ? Ox.extend({}, col) - : col; + ret = type == 'array' ? collection.slice() + : type == 'object' ? Ox.extend({}, collection) + : collection; } return ret; }; @@ -52,8 +54,10 @@ Ox.contains Tests if a collection contains a value true @*/ // FIXME: a shorter name would be nice (but IE8 doesn't like 'in') -Ox.contains = function(col, val) { - return (Ox.isObject(col) ? Ox.values(col) : col).indexOf(val) > -1; +Ox.contains = function(collection, value) { + return ( + Ox.isObject(collection) ? Ox.values(collection) : collection + ).indexOf(value) > -1; }; /*@ @@ -65,12 +69,12 @@ Ox.count Counts the occurences of values in a collection > Ox.count('foo') {f: 1, o: 2} @*/ -Ox.count = function(arr) { - var obj = {}; - Ox.forEach(arr, function(v) { - obj[v] = (obj[v] || 0) + 1; +Ox.count = function(collection) { + var ret = {}; + Ox.forEach(collection, function(value) { + ret[value] = (ret[value] || 0) + 1; }); - return obj; + return ret; }; /*@ @@ -86,8 +90,10 @@ Ox.every Tests if every element of a collection satisfies a given condition > Ox.every([true, true, true]) true @*/ -Ox.every = function(col, fn) { - return Ox.filter(Ox.values(col), fn || Ox.identity).length == Ox.len(col); +Ox.every = function(collection, iterator) { + return Ox.filter( + Ox.values(collection), iterator || Ox.identity + ).length == Ox.len(collection); }; /*@ @@ -101,18 +107,18 @@ Ox.filter Filters a collection by a given condition > Ox.filter(' foo bar ', function(v) { return v != ' '; }) 'foobar' @*/ -Ox.filter = function(col, fn, that) { - var ret, type = Ox.typeOf(col); - fn = fn || Ox.identity; +Ox.filter = function(collection, iterator, that) { + var ret, type = Ox.typeOf(collection); + iterator = iterator || Ox.identity; if (type == 'object') { ret = {}; - Ox.forEach(col, function(val, key) { - if (fn.call(that, val, key, col)) { - ret[val] = key; + Ox.forEach(collection, function(value, key) { + if (iterator.call(that, value, key, collection)) { + ret[value] = key; } }); } else { - ret = Ox.toArray(col).filter(fn, that); + ret = Ox.toArray(collection).filter(iterator, that); if (type == 'string') { ret = ret.join(''); } @@ -120,26 +126,6 @@ Ox.filter = function(col, fn, that) { return ret; }; -/*@ -Ox.find 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(); // fixme: don't loop twice!! - }).forEach(function(v, i) { - var index = v.indexOf(str); - index > -1 && ret[index == 0 ? 0 : 1][v == str ? 'unshift' : 'push'](arr[i]); - }); - return ret; -}; - /*@ // FIXME: documentation is outdated! Ox.forEach forEach loop @@ -165,148 +151,74 @@ Ox.forEach forEach loop > Ox.test.string "012abcfoo" @*/ -Ox.forEach = function(col, fn, that) { - var i, key, type = Ox.typeOf(col); +Ox.forEach = function(collection, iterator, that) { + var i, key, type = Ox.typeOf(collection); if (type != 'array' && type != 'object') { - col = Ox.toArray(col); + collection = Ox.toArray(collection); } try { if (type == 'object') { - for (key in col) { - // Ox.hasOwn(obj, key) && fn.call(that, col[key], key, col); - if (Ox.hasOwn(col, key) && fn.call(that, col[key], key, col) === false) { - throw new Error('Returning false in Ox.forEach is deprecated.'); + for (key in collection) { + if (Ox.hasOwn(collection, key)) { + // iterator.call(that, collection[key], key, collection); + if (iterator.call(that, collection[key], key, collection) === false) { + throw new Error('Returning false in Ox.forEach is deprecated.'); + } } } } else { - for (i = 0; i < col.length; i++) { - // i in col && fn.call(that, col[i], i, col); - if (i in col && fn.call(that, col[i], i, col) === false) { - throw new Error('Returning false in Ox.forEach is deprecated.'); + for (i = 0; i < collection.length; i++) { + if (i in collection) { + // iterator.call(that, collection[i], i, collection); + if (iterator.call(that, collection[i], i, collection) === false) { + throw new Error('Returning false in Ox.forEach is deprecated.'); + } } } } - } catch(e) { - if (e !== Ox.BreakError) { - throw e; + } catch(error) { + if (error !== Ox.BreakError) { + throw error; } } return type == 'object' ? key : i; }; /*@ -Ox.getIndexById Returns the first array index of an object with a given id - > Ox.getIndexById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'bar') - 1 - > Ox.getIndexById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'baz') - -1 -@*/ -Ox.getIndexById = function(arr, id) { - return Ox.indexOf(arr, function(obj) { - return obj.id === id; - }); -}; - -/*@ -Ox.getObjectById Returns the first object in an array with a given id - > Ox.getObjectById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'bar') - {id: "bar", str: "Bar"} - > Ox.getObjectById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'baz') - null -@*/ -Ox.getObjectById = function(arr, id) { - var index = Ox.getIndexById(arr, id); - return index > -1 ? arr[index] : null; -}; - -/*@ -Ox.getset Generic getter and setter function - See examples for details. - # Usage -------------------------------------------------------------------- - Ox.getset(options, args=[]) -> all options - Ox.getset(options, args=[key]) -> <*> options[key] - Ox.getset(options, args=[key, value], callback, context) -> 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) -> context - sets multiple options and calls fn(key, value) - for every key/value pair that was added or modified - # Arguments ---------------------------------------------------------------- - options Options object (key/value pairs) - args The arguments "array" of the caller function - callback Callback function - The callback is called for every key/value pair that was added or - modified. - key Key - value <*> Value - context The parent object of the caller function (for chaining) - # Examples ----------------------------------------------------------------- - - > 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.indexOf indexOf with a test function +Ox.indexOf Returns the first index of a collection element that passes a test > Ox.indexOf([1, 2, 3], function(val) { return val % 2 == 0; }) 1 - > Ox.indexOf('fooBar', function(val) { return val == val.toUpperCase(); }) - 3 + > Ox.indexOf({a: 1, b: 2, c: 3}, function(val) { return val % 2 == 0; }) + 'b' + > Ox.indexOf('FooBar', function(val) { return val == val.toUpperCase(); }) + 0 + > Ox.indexOf([1, 2, 3], function(val) { return val == 0; }) + -1 @*/ -Ox.indexOf = function(col, fn) { - var index = Ox.forEach(col, function(val) { - fn(val) && Ox.Break(); +Ox.indexOf = function(collection, test) { + var index = Ox.forEach(collection, function(value) { + test(value) && Ox.Break(); }); - return index == col.length ? -1 : index; + return index == collection.length ? -1 : index; }; /*@ -Ox.indicesOf return indices of array elements that pass a test - > Ox.indicesOf([1, 2, 3, 4], function(val) { return val % 2 == 0; }) - [1, 3] - +Ox.indicesOf Returns all indices of collection elements that pass a test + > Ox.indicesOf([1, 2, 3], function(val) { return val % 2 == 1; }) + [0, 2] + > Ox.indicesOf({a: 1, b: 2, c: 3}, function(val) { return val % 2 == 1; }) + ['a', 'c'] + > Ox.indicesOf('FooBar', function(val) { return val == val.toUpperCase(); }) + [0, 3] + > Ox.indicesOf([1, 2, 3], function(val) { return val == 0; }) + [] @*/ -Ox.indicesOf = function(col, fn) { - return Ox.map(col, function(val, i) { - return fn(val) ? i : null; - }).filter(function(index) { - return index !== null; +Ox.indicesOf = function(collection, test) { + var ret = []; + Ox.forEach(collection, function(value, index) { + test(value) && ret.push(index); }); + return ret; }; /*@ @@ -330,8 +242,8 @@ Ox.isEmpty Tests if a value is an empty array, object or string > Ox.isEmpty() false @*/ -Ox.isEmpty = function(val) { - return Ox.len(val) === 0; +Ox.isEmpty = function(value) { + return Ox.len(value) === 0; }; /*@ @@ -350,13 +262,13 @@ Ox.last Gets or sets the last element of an array > Ox.last('123') '3' @*/ -Ox.last = function(arr, val) { +Ox.last = function(array, value) { var ret; if (arguments.length == 1) { - ret = arr[arr.length - 1]; + ret = array[array.length - 1]; } else { - arr[arr.length - 1] = val; - ret = arr; + array[array.length - 1] = value; + ret = array; } return ret; }; @@ -382,59 +294,15 @@ Ox.len Returns the length of an array, node list, object or string undefined @*/ // FIXME: Ox.size() ? -Ox.len = function(col) { - var len, type = Ox.typeOf(col); +Ox.len = function(collection) { + var ret, type = Ox.typeOf(collection); if ( type == 'arguments' || type == 'array' || type == 'nodelist' || type == 'string' ) { - len = col.length; + ret = collection.length; } else if (type == 'object') { - len = Object.keys(col).length; - } - return len; -}; - -/*@ -Ox.makeArray Wraps any non-array in an array. - > Ox.makeArray('foo') - ['foo'] - > Ox.makeArray(['foo']) - ['foo'] -@*/ -Ox.makeArray = function(obj) { - var arr; - if (Ox.isArray(obj)) { - arr = obj; - } else if (Ox.isArguments(obj)) { - arr = Ox.toArray(obj); - } else { - arr = [obj]; - } - return arr; -}; - -/*@ -Ox.makeObject Takes an array and returns an object - Ox.makeObject is a helper for functions with two alternative - signatures like ('key', 'val') and ({key: 'val'}). - > (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(arr) { - var ret = {}, type = Ox.typeOf(arr[0]); - if (type == 'object') { - // ({foo: 'bar'}) - ret = arr[0]; - } else if (type == 'string') { - // ('foo', 'bar') - ret[arr[0]] = arr[1]; + ret = Object.keys(collection).length; } return ret; }; @@ -449,20 +317,18 @@ Ox.map Transforms the values of an array, object or string {a: true, b: false, c: false} > Ox.map('foo', function(v) { return v.toUpperCase(); }) 'FOO' - > Ox.map([0, 1, 2, 4], function(v, i) { return v ? i == v : null; }) - [true, true, false] > Ox.map([,], function(v, i) { return i; }) [,] @*/ -Ox.map = function(col, fn, that) { - var type = Ox.typeOf(col), ret; +Ox.map = function(collection, iterator, that) { + var ret, type = Ox.typeOf(collection); if (type == 'object') { ret = {}; - Ox.forEach(col, function(val, key) { - ret[key] = fn.call(that, val, key, col); + Ox.forEach(collection, function(value, key) { + ret[key] = iterator.call(that, value, key, collection); }); } else { - ret = Ox.toArray(col).map(fn); + ret = Ox.toArray(collection).map(iterator); if (type == 'string') { ret = ret.join(''); } @@ -479,13 +345,13 @@ Ox.max Returns the maximum value of a collection > Ox.max('123') 3 @*/ -Ox.max = function(col) { - var ret, values = Ox.values(col); +Ox.max = function(collection) { + var ret, values = Ox.values(collection); if (values.length < Ox.STACK_LENGTH) { ret = Math.max.apply(null, values) } else { - ret = values.reduce(function(pre, val) { - return Math.max(pre, val); + ret = values.reduce(function(previousValue, currentValue) { + return Math.max(previousValue, currentValue); }, -Infinity); } return ret; @@ -500,13 +366,13 @@ Ox.min Returns the minimum value of a collection > Ox.min('123') 1 @*/ -Ox.min = function(col) { - var ret, values = Ox.values(col); +Ox.min = function(collection) { + var ret, values = Ox.values(collection); if (values.length < Ox.STACK_LENGTH) { ret = Math.min.apply(null, values) } else { - ret = values.reduce(function(pre, val) { - return Math.min(pre, val); + ret = values.reduce(function(previousValue, currentValue) { + return Math.min(previousValue, currentValue); }, Infinity); } return ret; @@ -519,10 +385,10 @@ Ox.reverse Reverses an array or string > Ox.reverse('foobar') 'raboof' @*/ -Ox.reverse = function(col) { - return Ox.isArray(col) - ? Ox.clone(col).reverse() - : col.toString().split('').reverse().join(''); +Ox.reverse = function(collection) { + return Ox.isArray(collection) + ? Ox.clone(collection).reverse() + : collection.toString().split('').reverse().join(''); }; /*@ @@ -563,21 +429,21 @@ Ox.shuffle Randomizes the order of values within a collection > Ox.shuffle('123').split('').sort().join('') '123' @*/ -Ox.shuffle = function(col) { - var keys, ret, type = Ox.typeOf(col), values; +Ox.shuffle = function(collection) { + var keys, ret, type = Ox.typeOf(collection), values; if (type == 'object') { - keys = Object.keys(col); - values = Ox.shuffle(Ox.values(col)); + keys = Object.keys(collection); + values = Ox.shuffle(Ox.values(collection)); ret = {}; - keys.forEach(function(key, i) { - ret[key] = values[i] + keys.forEach(function(key, index) { + ret[key] = values[index]; }); } else { ret = []; - Ox.toArray(col).forEach(function(v, i) { - var random = Math.floor(Math.random() * (i + 1)); - ret[i] = ret[random]; - ret[random] = v; + Ox.toArray(collection).forEach(function(value, index) { + var random = Math.floor(Math.random() * (index + 1)); + ret[index] = ret[random]; + ret[random] = value; }); if (type == 'string') { ret = ret.join(''); @@ -591,8 +457,8 @@ Ox.slice Alias for Array.prototype.slice.call > (function() { return Ox.slice(arguments, 1, -1); }(1, 2, 3)) [2] @*/ -Ox.slice = function(val, start, stop) { - return Array.prototype.slice.call(val, start, stop); +Ox.slice = function(value, start, stop) { + return Array.prototype.slice.call(value, start, stop); }; /*@ @@ -608,10 +474,8 @@ Ox.some Tests if one or more elements of a collection meet a given condition > Ox.some([false, null, 0, '', void 0]) false @*/ -Ox.some = function(obj, fn) { - return Ox.filter(Ox.values(obj), fn || function(v) { - return v; - }).length > 0; +Ox.some = function(collection, iterator) { + return Ox.filter(Ox.values(collection), iterator || Ox.identity).length > 0; }; /*@ @@ -629,46 +493,16 @@ Ox.sum Returns the sum of the values of a collection > Ox.sum('08', -2, 'foo') 6 @*/ -Ox.sum = function(col) { - var sum = 0; - col = arguments.length > 1 ? Ox.toArray(arguments) : col; - Ox.forEach(col, function(val) { - val = +val; - sum += isFinite(val) ? val : 0; +Ox.sum = function(collection) { + var ret = 0; + collection = arguments.length > 1 ? Ox.toArray(arguments) : collection; + Ox.forEach(collection, function(value) { + value = +value; + ret += isFinite(value) ? value : 0; }); - return sum; + return ret; }; -/*@ -Ox.toArray Takes an array-like object and returns a true array - (value) -> True array - value <*> Array-like object - > (function() { return Ox.toArray(arguments); }("foo", "bar")) - ["foo", "bar"] - > Ox.toArray("foo") - ["f", "o", "o"] - > Ox.toArray({0: "f", 1: "o", 2: "o", length: 3}) - ["f", "o", "o"] -@*/ -// rewrite this so that it uses a try/catch test -Ox.toArray = /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.values Returns the values of a collection > Ox.values([1, 2, 3]) @@ -678,37 +512,45 @@ Ox.values Returns the values of a collection > Ox.values('abc') ['a', 'b', 'c'] > Ox.values([1,,3]) - [1, 3] + [1,,3] @*/ -Ox.values = function(col) { - var ret, type = Ox.typeOf(col); +Ox.values = function(collection) { + var ret, type = Ox.typeOf(collection); if (type == 'array') { - ret = col; + ret = collection; } else if (type == 'object') { ret = []; - Ox.forEach(col, function(val) { - ret.push(val); + Ox.forEach(collection, function(value) { + ret.push(value); }); } else if (type == 'string') { - ret = col.split(''); + ret = collection.split(''); } return ret; }; /*@ -Ox.walk Recursively walk a tree of key/value pairs +Ox.walk Iterate over a nested data structure > Ox.test.number 6 + > Ox.test.array + [['a'], ['b', 'c'], ['b', 'd']] @*/ -Ox.walk = function(obj, fn) { - Ox.forEach(obj, function(val, key) { - fn(val, key, obj); - Ox.walk(obj[key], fn); +Ox.walk = function(collection, iterator, keys) { + keys = keys || []; + Ox.forEach(collection, function(value, key) { + var keys_ = keys.concat(key); + iterator(value, keys_, collection); + Ox.walk(collection[key], iterator, keys_); }); };