Normalize array methods and return values

Is there a JavaScript Array library that normalizes the return values ​​of an array and mutation? I think the JavaScript Array API is very incompatible.

Some methods mutate an array:

var A = [0,1,2]; A.splice(0,1); // reduces A and returns a new array containing the deleted elements 

Some dont:

 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 will just return undefined:

 B = A.forEach(function(){}); 

I would like to always mutate the array and always return the same array, so I can have some consistency and also be able to chain. For instance:

 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 is great for most mutation methods, but there are problems. For example, I need to write my own prototypes for each method that does not mutate the original array.

So, as always, when I did this, I thought that perhaps this was done before? Are there any lightweight array libraries that already do this? It would be nice if the library also added pads for the new JavaScript 1.6 for older browsers.

+4
source share
3 answers

I do not think this is really inconsistent. Yes, they can be a bit confusing, because JavaScript arrays do everything for which other languages ​​have separate structures (list, queue, stack, ...), but their definition is quite consistent between the languages. You can easily group them in the categories that you have already described:

  • list of methods:
    • push / unshift returns the length after adding elements
    • pop / shift returns the requested item
    • you could define additional methods to get the first and last element, but they are rarely needed
  • splice is a universal tool for removing / replacing / inserting elements in the middle of the list - it returns an array of deleted elements.
  • sort and reverse are two standard in-place reordering methods.

All other methods do not modify the original array:

  • slice to get subarrays by position, filter to get them by condition and concat to merge with other creatures and return new arrays
  • forEach just forEach through the array and returns nothing
  • every / some check the elements for the condition, indexOf and lastIndexOf look for the elements (according to the principle of equality) - both return their results
  • reduce / reduceRight reduces the elements of the array to a single value and returns this. Special cases:
    • map reduces to a new array - it is similar to forEach , but returns the results
    • join and toString reduce to string

These methods are sufficient for most of our needs. We can do everything with them, and I don’t know any libraries that would add similar, but internal or resultant methods to them. Most data processing libraries (e.g. Underscore ) only make them cross-browser ( es5-shim ) and provide additional utilities.

I want to always mutate the array and always return the same array, so I can have some consistency and also be able to chain.

I would say that JavaScript consistency is to always return a new array when elements or lengths change. I suppose this is because objects are reference values, and changing them too often causes side effects in other areas that reference the same array.

Chaining is still possible with this, you can use slice , concat , sort , reverse , filter and map together to create a new array in just one step. If you want to "modify" an array only, you can simply reassign it to an array variable:

 A = A.slice(0,1).reverse().concat(['a','b']); 

Mutation methods have only one advantage for me: they are faster because they can be more memory efficient (of course, it depends on the implementation and garbage collection). Therefore, it allows you to implement some methods for them. Since the Array Subclass is neither possible nor useful, I will define them on a native prototype :

 var ap = Array.prototype; // the simple ones: ap.each = function(){ ap.forEach.apply(this, arguments); return this; }; ap.prepend = function() { ap.unshift.apply(this, arguments); return this; }; ap.append = function() { ap.push.apply(this, arguments; return this; }; ap.reversed = function() { return ap.reverse.call(ap.slice.call(this)); }; ap.sorted = function() { return ap.sort.apply(ap.slice.call(this), arguments); }; // more complex: ap.shorten = function(start, end) { // in-place slice if (Object(this) !== this) throw new TypeError(); var len = this.length >>> 0; start = start >>> 0; // actually should do isFinite, then floor towards 0 end = typeof end === 'undefined' ? len : end >>> 0; // again start = start < 0 ? Math.max(len + start, 0) : Math.min(start, len); end = end < 0 ? Math.max(len + end, 0) : Math.min(end, len); ap.splice.call(this, end, len); ap.splice.call(this, 0, start); return this; }; ap.restrict = function(fun) { // in-place filter // while applying fun the array stays unmodified var res = ap.filter.apply(this, arguments); res.unshift(0, this.length >>> 0); ap.splice.apply(this, res); return this; }; ap.transform = function(fun) { // in-place map if (Object(this) !== this || typeof fun !== 'function') throw new TypeError(); var len = this.length >>> 0, thisArg = arguments[1]; for (var i=0; i<len; i++) if (i in this) this[i] = fun.call(thisArg, this[i], i, this) return this; }; // possibly more 

Now you can do

 A.shorten(0, 1).reverse().append('a', 'b'); 
+2
source

IMHO one of the best libraries - underscorejs http://underscorejs.org/

+1
source

You should probably not use the library just for this (this is not a very useful dependency to add to the project).

The "standard" way is to call slice when you want to perform mutate operations. This is not a problem, since JS engines are pretty good with temporary variables (since this is one of the key points of javascript).

Ex .:

 function reverseStringify( array ) { return array.slice( ) .reverse( ) .join( ' ' ); } console.log( [ 'hello', 'world' ] ); 
0
source

Source: https://habr.com/ru/post/1442949/


All Articles