Last active
February 7, 2016 23:02
-
-
Save a-c-t-i-n-i-u-m/8a331f807d0329c66c5d to your computer and use it in GitHub Desktop.
generic polyfill/alternative iteration method for javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// polyfill/alternative method for iteration | |
// - breakable when callback 'return false;', quit iteration immediately. | |
// - capable only depends on numerical 'length' property, or iterate with keys. | |
// - flexible third argument 'thisArg' and return value | |
// make you possible to write efficient code with this method. | |
// - | |
var each = function (obj, callback, thisArg) { | |
// check object, callback function | |
if (!obj || typeof callback !== 'function') { | |
return; | |
} | |
// when 'thisArg' is given, or use 'obj' | |
thisArg = arguments.length === 3 ? thisArg : obj; | |
// mode; if numerical length property exists, loop as array | |
if (!isNaN(obj.length)) { | |
var len = +obj.length; | |
for (var i = 0; i < len; i++) { | |
if (callback.call(thisArg, obj[i], i, obj) === false) { | |
break; | |
} | |
} | |
} | |
// mode; or, iterate by object keys | |
else { | |
for (var k in obj) { | |
// check property | |
if (Object.prototype.hasOwnProperty.call(obj, k)) { | |
if (callback.call(thisArg, obj[k], k, obj) === false) { | |
break; | |
} | |
} | |
} | |
} | |
// return thisArg object; important for flexible and powerful iteration | |
return thisArg; | |
}; | |
// | |
// usage/example/test codes | |
// | |
// thisArg | |
var t1 = [], | |
t2 = []; | |
console.assert(each(t1, function () {}) === t1); | |
console.assert(each(t1, function () {}, t2) === t2); | |
// usage | |
// basic | |
// simple loop | |
var sourceArray = [3, 10, 29, 55, 102, 876]; | |
each(sourceArray, function (val, index, obj) { | |
console.assert(this === obj && val === obj[index]); | |
}); | |
// obj key loop | |
var sourceObj = {a: 1, b: 2, c: 3}; | |
each(sourceObj, function (val, key, obj) { | |
console.assert(this === obj && val === obj[key]); | |
}); | |
// break example | |
// Array.prototype.indexOf | |
var arrayIndexOf = function (arr, search) { | |
var result = -1; | |
each(arr, function (v, i) { | |
if (v === search) { | |
result = i; | |
return false;// break 'each' loop | |
} | |
}); | |
return result; | |
}; | |
// if you think 'return false;' is confusing, define some global variable as false | |
var BREAK = false; | |
// then, return it | |
each(sourceArray, function (v) { | |
if (v === 10) { | |
return BREAK; | |
} | |
}); | |
// extend | |
// Array.prototype.filter alternative | |
var sourceArray = [3, 10, 34, 55, 102, 876]; | |
var filterdArray = each(sourceArray, function (n) { | |
if (n < 30) this.push(n); | |
}, []); | |
console.assert(filterdArray.length === 2 && filterdArray[0] === 3 && filterdArray[1] === 10); | |
// related; array.unique | |
var arrayHasDuplicatedElements = [1, 1, 2, 2, 3]; | |
var unique = each(arrayHasDuplicatedElements, function (v) { | |
if (this.indexOf(v) === -1) this.push(v); | |
}, []); | |
console.assert(unique.length === 3 && unique[0] === 1 && unique[1] === 2 && unique[2] === 3); | |
// related; array.intersect | |
var sourceArray2 = [100, 200, 300, 10, 1, 2, 3]; | |
var arrayIntersect = function (a, b) { | |
return each(a, function (v) { | |
if (b.indexOf(v) !== -1) { | |
this.push(v); | |
} | |
}, []); | |
}; | |
var intersect1_2 = arrayIntersect(sourceArray, sourceArray2); | |
console.assert(intersect1_2.length === 2 && intersect1_2[0] === 3 && intersect1_2[1] === 10); | |
// related; array.remove([remove, elements, elements, ...]) | |
var removes = [10000, 1000, 100, 10, 1]; | |
var arrayRemove = function (source, remove) { | |
return each(source, function (v) { | |
if (remove.indexOf(v) === -1) {// <-- !! only difference between arrayIntersect and arrayRemove; | |
this.push(v); | |
} | |
}, []); | |
}; | |
// break sample | |
// Array.prototype.some/every | |
var arrayTest = function (arr, tests, some) { | |
var result = !some; | |
each(arr, function (v, i, a) { | |
var t = tests(v, i, a); | |
// if some = true, if some element passes the test, return true; | |
if (some && t) { | |
result = true; | |
return false;// break | |
} | |
// if some = false; 'every' mode; if some elements dosen't passes the test, return false; | |
if (result && !t) { | |
result = false; | |
return false;// break | |
} | |
}); | |
return result; | |
}; | |
console.assert(arrayTest(sourceArray, function (v) { return v < 100; }) === false); | |
console.assert(arrayTest(sourceArray, function (v) { return v === 55; }, true) === true); | |
// objects | |
// Shallow copy of object | |
var clonedObj = each(sourceObj, function (v, k) { this[k] = v; }, {}); | |
console.assert(sourceObj !== clonedObj); | |
each(sourceObj, function (v, k) { | |
console.assert(v === clonedObj[k]); | |
}); | |
// related; merge object to new object | |
var mergeObject = function () { | |
return each(arguments, function (o) { | |
each(o, function (v, k) { | |
this[k] = v; | |
}, this); | |
}, {}); | |
}; | |
var sourceObj2 = {c: 6, d: 7, e: 9}, | |
sourceObj3 = {aaa: 'string', bbb: sourceArray, ccc: clonedObj}; | |
var mergedObject = mergeObject(sourceObj, sourceObj2, sourceObj3); | |
console.assert(sourceObj !== mergedObject && sourceObj2 !== mergeObject && sourceObj3 !== mergedObject); | |
// generate random numbers | |
var randomIntArray = function (len, min, max) { | |
if (isNaN(len) || len <= 0) { | |
return []; | |
} | |
min = min || 0; | |
var u = (max || 1) - min + 1 | |
return each(new Array(+len), function (c, i) { | |
this[i] = Math.floor(Math.random() * u) + min; | |
}); | |
}; | |
// tricky | |
// use 'thisArg' object as 'call by reference' | |
// string conversion; capitalize the first character of each word | |
var resultStr = each('this is test text', function (c) { | |
this.result += this.nextCapitalize ? c.toUpperCase() : c; | |
this.nextCapitalize = c === ' '; | |
}, {result: '', nextCapitalize: true}).result; | |
console.assert(resultStr === 'This Is Test Text'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment