Lists of all sorts (e.g. grocery, to-do, grades etc) are a normal facet of our daily lives. Their ubiquitousness extends to software engineering as many programming tasks require manipulating lists. This post exposes new ways of doing popular list manipulation tasks in JavaScript and a little more too.
Be warned! JavaScript array methods routinely expose amazing surprises. Other languages generally tend not to have such hidden surprises, but well, don’t you just love the extra excitement from JavaScript? Afterall, it’s the language we all love to hate ;).
1. Appending arrays
One of the most common tasks is appending an array to the end of another array; Array.prototype.concat works but forces you to create a temporary variable or overwrite one of the arrays.
A clever use of Array.prototype.push allows getting by both limitations.
var a = [1,2,3];
var b = [4,5,6];
//requires temp value or overwriting
a = a.concat(b);
//using push
a.push.apply(a, b);
//similar variations
[].push.apply(a,b);
Array.prototype.push.apply(a,b);
How does this work? The Array.prototype.push method will append its arguments to the end of the array it’s called on.
a=[];
a.push(1,2,3);
a;
//[1,2,3]
The Function.prototype.apply method applies a method to an object and overrides arguments (an array-like object containing the arguments passed to a function). Apply allows you to invoke a function on any object with whatever parameters you want.
The problem is thus solved by applying push to a and providing b as the arguments to be pushed. The code snippet is the same as shown below:
a.push(b1, b2, ..., bn);
This trick can be generalized and used anywhere once the second array can be transformed into an arguments object. The Math.max and Math.min methods, which return the max/min value of a set, provide another example.
Using the trick above, an array’s max value can be found by applying the Math.max method on the array.
var a = [1,2,3,4,5];
var max = Math.max.apply(Math, a);
max;
//5
Note that the object has to be Math here since it contains the max function. Isn’t this neater than iterating over the array to find the max value?
Tip: Apply differs from Call which expects an array; if Call were to be used, then b gets inserted as a single element. Can you figure out how to use Call to achieve the same results?
2. Pushing into objects
Do you know Array.prototype.push works on non-array objects too? Huh? Didn’t I warn about JS Array methods earlier on?
Push is a generic function and provided the object it is applied to meets its requirements it’ll work; otherwise, tell me when you find out.
An example is shown below (anyone that does this in real-life code should be barred from touching code for weeks…).
var obj = {};
[].push.apply(obj, [1]);
Array.prototype.push.apply(obj, [2,3,4]);
obj;
//{0: 1, 1: 2, 2: 3, 3: 4, length: 4}
How did the length property get there?! The ES5 push spec explains this: the 6th step of the push operation will put length and the number of elements as a key-value pair into the object push was called on. Do you now know why it is [].length and not [].length()?
The tale continues, how about splicing some lists?
3. Merging arrays and removing elements at specified locations
The Array.prototype.splice method is probably mostly used for removing elements from JS arrays. Why it’s called splice and not remove is baffling but since it allows you to simultaneously insert elements I guess that name works. You can splice rope strands huh…
You can actually ‘delete’ array entries using the delete operator, however delete will leave behind undefined at the deleted element’s index; typically not what you want in most scenarios.
SideTip: The delete operator accepts expressions as operands and will evaluate such expression operands! E.g. delete (b=4) works!!
var a = [1,2,3];
delete a[1];
a;
//[1, undefined, 3]
a.length;
//3
Fix? Lets splice!
var a = [1,2,3];
var removed = a.splice(1,1);
removed;
//[2]
a;
[1,3]
The splice operation can also be used to insert an array’s elements into another array at a certain index, how? You probably guessed that now – it is the apply trick again.
var a =[1,2,6];
var b = [3,4,5]
a.splice.apply(a, [].concat(2, 0, b));
a;
//[1,2,3,4,5,6]
Confused about the need for [].concat? Apply expects an array of parameters that match the function’s signature; these are passed through as the arguments. The splice call to merge the arrays has the 2, the position to insert at; 0, the number of elements to remove and b, the array of elements to be merged.
The concat call translates into [2,0,4,5,6] and this is equivalent to the call below:
a.splice(2,0,4,5,6);
4. Truncating and Emptying arrays
var a = [1,2,3];
a.length = 0;
//Truncating
a;
//[]
a.length = 2;
a;
//[1,2]
//Or filling up arrays with undefined
a.length = 3;
a;
[1,2,undefined]
The ES5 spec explain this, once length is set to a value less than the original length, all outlying elements will be deleted.
So why did I write the post?
1. So you know what to do if you ever run into these ‘smart’ pieces of code
2. To expose some of the other side of JS.
Hope you liked it, here are some more fun posts ;)
1. Three Important JavaScript Concepts
2. Understanding Tail Recursion
3. Understanding Partial Application and Currying
2 thoughts on “Manipulating lists with JavaScript Array methods”