From 3d4a79b6a2e073cce5aeef72757a9555c19df9f6 Mon Sep 17 00:00:00 2001 From: rolux Date: Mon, 21 May 2012 22:17:33 +0200 Subject: [PATCH] in Ox.every, use Ox.identity; faster Ox.filter; in Ox.forEach, allow for Ox.break, and use standard arguments for iterator function, remove Ox.getIndex and Ox.getObject; add Ox.indexOf; in Ox.makeObject, use Ox.typeOf; make sure Ox.max and Ox.min don't blow the stack; add Ox.slice; in Ox.values, use Ox.typeOf --- source/Ox/js/Collection.js | 213 ++++++++++++++++++++----------------- 1 file changed, 118 insertions(+), 95 deletions(-) diff --git a/source/Ox/js/Collection.js b/source/Ox/js/Collection.js index 5a355dc6..6499b9cd 100644 --- a/source/Ox/js/Collection.js +++ b/source/Ox/js/Collection.js @@ -92,9 +92,7 @@ Ox.every Tests if every element of a collection satisfies a given condition true @*/ Ox.every = function(col, fn) { - return Ox.filter(Ox.values(col), fn || function(v) { - return v; - }).length == Ox.len(col); + return Ox.filter(Ox.values(col), fn || Ox.identity).length == Ox.len(col); }; /*@ @@ -108,20 +106,21 @@ Ox.filter Filters a collection by a given condition > 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(val, key) { - if (fn(val, key)) { - if (type == 'array') { - ret.push(val); - } else if (type == 'object') { - ret[key] = val; - } else { - ret += val; +Ox.filter = function(col, fn, that) { + var ret, type = Ox.typeOf(col); + if (type == 'object') { + ret = {}; + Ox.forEach(col, function(val, key) { + if (fn.call(that, val, key, col)) { + ret[val] = key; } + }); + } else { + ret = Ox.toArray(col).filter(fn, that); + if (type == 'string') { + ret = ret.join(''); } - }); + } return ret; }; @@ -169,82 +168,60 @@ Ox.forEach forEach loop > Ox.test.string "012abcfoo" @*/ -// fixme: see http://stackoverflow.com/questions/2641347/javascript-array-foreach-howto-break -// maybe throwing an exception (Ox.break()?) is better than returning false -Ox.forEach = function(col, fn, includePrototype) { - var ind = 0, isObject = Ox.isObject(col), key; - // Safari will not loop through an arguments array - col = Ox.isArguments(col) ? Ox.toArray(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, ind++) === false) { - break; +Ox.forEach = function(col, fn, that) { + var i, key, type = Ox.typeOf(col); + if (type != 'array' && type != 'object') { + col = Ox.toArray(col); + } + 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) { + console.warn('Returning false in Ox.forEach is deprecated.') + break; + } + } + } else { + for (i = 0; i < col.length; i++) { + // fn.call(that, col[i], i, col); + if (fn.call(that, col[i], i, col) === false) { + console.warn('Returning false in Ox.forEach is deprecated.') + break; + } + } + } + } catch(e) { + if (e !== Ox.BreakError) { + throw e; } } - return col; -}; - -/*@ -Ox.getIndex Returns the first array index of an object where obj[key] is val - > Ox.getIndex([{a: 1}, {a: 2}, {a: 1}], 'a', 2) - 1 - > Ox.getIndex([{a: 1}, {a: 2}, {a: 1}], 'a', 1) - 0 - > Ox.getIndex([{a: 1}, {a: 2}, {a: 1}], 'a', 0) - -1 -@*/ -Ox.getIndex = function(arr, key, val) { - var ret = -1; - Ox.forEach(arr, function(obj, ind) { - if (obj[key] === val) { - ret = ind; - return false; - } - }); - return ret; + 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'}], 'foo') - 0 + > 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) { - // FIXME: shouldn't this be merged into Ox.getIndex? - return Ox.getIndex(arr, 'id', id); -}; - -/*@ -Ox.getObject Returns the first object in an array where obj[key] is val - > Ox.getObject([{a: 1, i: 0}, {a: 2, i: 1}, {a: 1, i: 2}], 'a', 2) - {a: 2, i: 1} - > Ox.getObject([{a: 1, i: 0}, {a: 2, i: 1}, {a: 1, i: 2}], 'a', 1) - {a: 1, i: 0} - > Ox.getObject([{a: 1, i: 0}, {a: 2, i: 1}, {a: 1, i: 2}], 'a', 0) - null -@*/ -Ox.getObject = function(arr, key, val) { - var ret = null; - Ox.forEach(arr, function(obj) { - if (obj[key] === val) { - ret = obj; - return false; - } + return Ox.indexOf(arr, function(obj) { + return obj.id === id; }); - return ret; }; /*@ Ox.getObjectById Returns the first object in an array with a given id - > Ox.getObjectById([{id: 'foo', str: 'Foo'}, {id: 'bar', str: 'Bar'}], 'foo') - {id: "foo", str: "Foo"} + > 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) { - // FIXME: shouldn't this be merged into Ox.getObject? - return Ox.getObject(arr, 'id', id); + var index = Ox.getIndexById(arr, id); + return index > -1 ? arr[index] : null; }; /*@ @@ -309,6 +286,20 @@ Ox.getset = function(obj, args, callback, context) { return ret; } +/*@ +Ox.indexOf indexOf with a test function + > Ox.indexOf([1, 2, 3], function(val) { return val % 2 == 0; }) + 1 + > Ox.indexOf('fooBar', function(val) { return val == val.toUpperCase(); }) + 3 +@*/ +Ox.indexOf = function(col, fn) { + var index = Ox.forEach(col, function(val) { + fn(val) && Ox.break(); + }); + return index == col.length ? -1 : index; +}; + /*@ Ox.isEmpty Tests if a value is an empty array, object or string (value) -> True if the value is an empty array, object or string @@ -361,8 +352,8 @@ Ox.keys = function(obj) { /*@ Ox.last Gets or sets the last element of an array - Unlike foobarbaz[foobarbaz.length - 1], - Ox.last(foobarbaz) is short. + Unlike arrayWithALongName[arrayWithALongName.length - 1], + Ox.last(arrayWithALongName) is short. @@ -372,6 +363,8 @@ Ox.last Gets or sets the last element of an array [1, 2, 4] > Ox.test.array [1, 2, 4] + > Ox.last('123') + '3' @*/ Ox.last = function(arr, val) { var ret; @@ -449,14 +442,14 @@ Ox.makeObject Takes an array and returns an object > (function() { return Ox.makeObject(arguments); }()) {} @*/ -Ox.makeObject = function(obj) { - var ret = {}; - if (Ox.isObject(obj[0])) { +Ox.makeObject = function(arr) { + var ret = {}, type = Ox.typeOf(arr[0]); + if (type == 'object') { // ({foo: 'bar'}) - ret = obj[0]; - } else if (obj.length) { + ret = arr[0]; + } else if (type == 'string') { // ('foo', 'bar') - ret[obj[0]] = obj[1] + ret[arr[0]] = arr[1]; } return ret; }; @@ -506,7 +499,15 @@ Ox.max Returns the maximum value of a collection 3 @*/ Ox.max = function(col) { - return Math.max.apply(Math, Ox.values(col)); + var ret, values = Ox.values(col); + if (values.length < Ox.STACK_LENGTH) { + ret = Math.max.apply(null, values) + } else { + ret = values.reduce(function(pre, val) { + return Math.max(pre, val); + }, -Infinity); + } + return ret; }; /*@ @@ -519,7 +520,15 @@ Ox.min Returns the minimum value of a collection 1 @*/ Ox.min = function(col) { - return Math.min.apply(Math, Ox.values(col)); + var ret, values = Ox.values(col); + if (values.length < Ox.STACK_LENGTH) { + ret = Math.min.apply(null, values) + } else { + ret = values.reduce(function(pre, val) { + return Math.min(pre, val); + }, Infinity); + } + return ret; }; /*@ @@ -595,7 +604,16 @@ Ox.shuffle = function(col) { }; /*@ -Ox.some Tests if one or more elements of a collection satisfy a given condition +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.some Tests if one or more elements of a collection meet a given condition Unlike [].some(), Ox.some() works for arrays, objects and strings. > Ox.some([2, 1, 0], function(i, v) { return i == v; }) @@ -604,7 +622,7 @@ Ox.some Tests if one or more elements of a collection satisfy a given condit true > Ox.some("foo", function(v) { return v == 'f'; }) true - > Ox.some([false, 0, null, '', void 0]) + > Ox.some([false, null, 0, '', void 0]) false @*/ Ox.some = function(obj, fn) { @@ -670,7 +688,6 @@ Ox.sum = function(col) { /*@ Ox.toArray Takes an array-like object and returns a true array - Alias for Array.prototype.slice.call (value) -> True array value <*> Array-like object > (function() { return Ox.toArray(arguments); }("foo", "bar")) @@ -711,12 +728,18 @@ Ox.values Returns the values of a collection [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; + var ret, type = Ox.typeOf(col); + if (type == 'array') { + ret = col; + } else if (type == 'object') { + ret = []; + Ox.forEach(col, function(val) { + ret.push(val); + }); + } else if (type == 'string') { + ret = col.split(''); + } + return ret; }; /*@