Is there any JavaScript Array library that normalizes the Array return values and mutations? I think the JavaScript Array API is very inconsistent.
Some methods mutate the array:
var A = [0,1,2];
A.splice(0,1); // reduces A and returns a new array containing the deleted elements
Some don’t:
A.slice(0,1); // leaves A untouched and returns a new array
Some return a reference to the mutated array:
A = A.reverse().reverse(); // reverses and then reverses back
Some just return undefined:
B = A.forEach(function(){});
What I would like is to always mutate the array and always return the same array, so I can have some kind of consistency and also be able to chain. For example:
A.slice(0,1).reverse().forEach(function(){}).concat(['a','b']);
I tried some simple snippets like:
var superArray = function() {
this.length = 0;
}
superArray.prototype = {
constructor: superArray,
// custom mass-push method
add: function(arr) {
return this.push.apply(this, arr);
}
}
// native mutations
'join pop push reverse shift sort splice unshift map forEach'.split(' ').forEach(function(name) {
superArray.prototype[name] = (function(name) {
return function() {
Array.prototype[name].apply(this, arguments);
// always return this for chaining
return this;
};
}(name));
});
// try it
var a = new superArray();
a.push(3).push(4).reverse();
This works fine for most mutation methods, but there are problems. For example I need to write custom prototypes for each method that does not mutate the original array.
So as always while I was doing this, I was thinking that maybe this has been done before? Are there any lightweight array libraries that do this already? It would be nice if the library also adds shims for new JavaScript 1.6 methods for older browsers.
I don’t think it is really inconsistent. Yes, they might be a little confusing as JavaScript arrays do all the things for which other languages have separate structures (list, queue, stack, …), but their definition is quite consistent across languages. You can easily group them in those categories you already described:
push/unshiftreturn the length after adding elementspop/shiftreturn the requested elementspliceis the all-purpose-tool for removing/replacing/inserting items in the middle of the list – it returns an array of the removed elements.sortandreverseare the two standard in-place reordering methods.All the other methods do not modify the original array:
sliceto get subarrays by position,filterto get them by condition andconcatto combine with others create and return new arraysforEachjust iterates the array and returns nothingevery/sometest the items for a condition,indexOfandlastIndexOfsearch for items (by equality) – both return their resultsreduce/reduceRightreduce the array items to a single value and return that. Special cases are:mapreduces to a new array – it is likeforEachbut returning the resultsjoinandtoStringreduce to a stringThese methods are enough for the most of our needs. We can do quite everything with them, and I don’t know any libraries that add similar, but internally or result-wise different methods to them. Most data-handling libs (like Underscore) only make them cross-browser-safe (es5-shim) and provide additional utility methods.
I’d say the JavaScript consistency is to alway return a new array when elements or length are modified. I guess this is because objects are reference values, and changing them would too often cause side effects in other scopes that reference the same array.
Chaining is still possible with that, you can use
slice,concat,sort,reverse,filterandmaptogether to create a new array in only one step. If you want to “modify” the array only, you can just reassign it to the array variable:Mutation methods have only one advantage to me: they are faster because they might be more memory-efficient (depends on the implementation and its garbage collection, of course). So lets implement some methods for those. As Array subclassing is neither possible nor useful, I will define them on the native prototype:
Now you could do