I have developed the following method for each as a replacement for $.each, and I am considering using it in my personal lightweight js library called LIBJS.
The final version will be part of a javascript class as opposed to a stand alone function.
/**
*
* @method each
* @param {Object} obj { Either an obj array or an object literal to be searched}
* @param {Function} fn { The method that has access to the searched value if exists }
* @param {Function} condition [condition=false] { The condition that is used to filter the object or array }
* @return {Number} { The number of matches found in the given obj or false if none found }
*/
function each(obj, fn, condition) {
var matches;
if (typeof obj !== 'object') return; // Filter only objects and object arrays
function eachObj(obj, fn, condition) {
function parseAll() {
for (var key in obj)
if (obj.hasOwnProperty(key)) fn(obj[key], key);
}
function parseConditional() {
for (var key in obj)
if (obj.hasOwnProperty(key) && condition(obj[key], key)) {
fn(obj[key], key);
++matches;
}
}
typeof condition === 'function' ? parseConditional() : parseAll();
return matches;
}
function eachArr(obj, fn, condition) {
if (!condition) obj.filter(fn);
else obj.filter(function(idx, val) {
if (condition(idx, val)) fn(idx, val);
});
return matches;
}
return Array.isArray(obj) ? eachArr(obj, fn, condition) : eachObj(obj, fn, condition);
}
I feel that this approach is better than John Resigs $.each because:
It works on nodeLists, Arrays and normal Objects using the exact same API. I feel that this makes it more intuitive because regardless of the data structure, the developer can use the same call for the same overall purpose.
I allow the programmer to use the Array.prototype.filter style signature and logic but with an object! i noticed that the primary reason I loop through objects is the either do a comparison, make a modification or read a value, etc.
The method call when organized this way allows complete separation of the filtering logic from the logic that will be applied to the matching value or key. Furthermore, absence of the filter function will result in traditional $.each behavior (all members of the collection would be iterated as opposed to conditionally applied).
Additionally, I have included my $.find and $.replace replacements are powered by each as I imagine the rest of my lib will be as well. This is just to give more idea on how the each is implemented.
function find(subject, searchTerm, method, lastMatchCt){
var totalMatches = lastMatchCt || 0;
function searchEach(subject, searchSubj, method, lastMatchCt){
each(subject, function(val, key){
if (typeof val === 'object' && !Array.isArray(val)) {
searchEach(val, searchSubj, method, totalMatches);
}
else if (val === searchSubj){
method(subject, key);
++totalMatches;
}
}, function(val){
if (val === searchTerm || typeof val === 'object') return true;
});
return totalMatches
}
return searchEach(subject, searchTerm, method, lastMatchCt);
}
function replace(source, search, replacement, customFilter){
var filter = customFilter || function(objLoc, key){
objLoc[key] = replacement;
};
return find(source, search, filter);
}
Last but not least, my version of each()
returns the number of matches found when the dev uses the conditional function parameter.
My basic idea was to make a method that allows you to track and iterate ANY type of collection. Do you think that this each is more useful/better than John Resigs $.each? Am I missing something that John Resigs implementation included? Are the any performance/efficiency optimizations that could be introduced?