Skip to content

Instantly share code, notes, and snippets.

@a-c-t-i-n-i-u-m
Last active February 7, 2016 23:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save a-c-t-i-n-i-u-m/8a331f807d0329c66c5d to your computer and use it in GitHub Desktop.
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
// 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