I've run into the following code in the es-discuss mailing list:
Array.apply(null, { length: 5 }).map(Number.call, Number);
This produces
[0, 1, 2, 3, 4]
Why is this the result of the code? What's happening here?
Understanding this "hack" requires understanding several things:
Why we don't just do Array(5).map(...)
How Function.prototype.apply handles arguments
How Array handles multiple arguments
How the Number function handles arguments
What Function.prototype.call does
They're rather advanced topics in javascript, so this will be more-than-rather long. We'll start from the top. Buckle up!
1. Why not just Array(5).map?
What's an array, really? A regular object, containing integer keys, which map to values. It has other special features, for instance the magical length variable, but at it's core, it's a regular key => value map, just like any other object. Let's play with arrays a little, shall we?
var arr = ['a', 'b', 'c'];
arr.hasOwnProperty(0); //true
arr[0]; //'a'
Object.keys(arr); //['0', '1', '2']
arr.length; //3, implies arr[3] === undefined
//we expand the array by 1 item
arr.length = 4;
arr[3]; //undefined
arr.hasOwnProperty(3); //false
Object.keys(arr); //['0', '1', '2']
We get to the inherent difference between the number of items in the array, arr.length, and the number of key=>value mappings the array has, which can be different than arr.length.
Expanding the array via arr.length does not create any new key=>value mappings, so it's not that the array has undefined values, it does not have these keys. And what happens when you try to access a non-existent property? You get undefined.
Now we can lift our heads a little, and see why functions like arr.map don't walk over these properties. If arr[3] was merely undefined, and the key existed, all these array functions would just go over it like any other value:
//just to remind you
arr; //['a', 'b', 'c', undefined];
arr.length; //4
arr[4] = 'e';
arr; //['a', 'b', 'c', undefined, 'e'];
arr.length; //5
Object.keys(arr); //['0', '1', '2', '4']
arr.map(function (item) { return item.toUpperCase() });
//["A", "B", "C", undefined, "E"]
I intentionally used a method call to further prove the point that the key itself was never there: Calling undefined.toUpperCase would have raised an error, but it didn't. To prove that:
arr[5] = undefined;
arr; //["a", "b", "c", undefined, "e", undefined]
arr.hasOwnProperty(5); //true
arr.map(function (item) { return item.toUpperCase() });
//TypeError: Cannot call method 'toUpperCase' of undefined
And now we get to my point: How Array(N) does things. Section 15.4.2.2 describes the process. There's a bunch of mumbo jumbo we don't care about, but if you manage to read between the lines (or you can just trust me on this one, but don't), it basically boils down to this:
function Array(len) {
var ret = [];
ret.length = len;
return ret;
}
(operates under the assumption (which is checked in the actual spec) that len is a valid uint32, and not just any number of value)
So now you can see why doing Array(5).map(...) wouldn't work - we don't define len items on the array, we don't create the key => value mappings, we simply alter the length property.
Now that we have that out of the way, let's look at the second magical thing:
2. How Function.prototype.apply works
What apply does is basically take an array, and unroll it as a function call's arguments. That means that the following are pretty much the same:
function foo (a, b, c) {
return a + b + c;
}
foo(0, 1, 2); //3
foo.apply(null, [0, 1, 2]); //3
Now, we can ease the process of seeing how apply works by simply logging the arguments special variable:
function log () {
console.log(arguments);
}
log.apply(null, ['mary', 'had', 'a', 'little', 'lamb']);
//["mary", "had", "a", "little", "lamb"]
//arguments is a pseudo-array itself, so we can use it as well
(function () {
log.apply(null, arguments);
})('mary', 'had', 'a', 'little', 'lamb');
//["mary", "had", "a", "little", "lamb"]
//a NodeList, like the one returned from DOM methods, is also a pseudo-array
log.apply(null, document.getElementsByTagName('script'));
//[script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script]
//carefully look at the following two
log.apply(null, Array(5));
//[undefined, undefined, undefined, undefined, undefined]
//note that the above are not undefined keys - but the value undefined itself!
log.apply(null, {length : 5});
//[undefined, undefined, undefined, undefined, undefined]
It's easy to prove my claim in the second-to-last example:
function ahaExclamationMark () {
console.log(arguments.length);
console.log(arguments.hasOwnProperty(0));
}
ahaExclamationMark.apply(null, Array(2)); //2, true
(yes, pun intended). The key => value mapping may not have existed in the array we passed over to apply, but it certainly exists in the arguments variable. It's the same reason the last example works: The keys do not exist on the object we pass, but they do exist in arguments.
Why is that? Let's look at Section 15.3.4.3, where Function.prototype.apply is defined. Mostly things we don't care about, but here's the interesting portion:
Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
Which basically means: argArray.length. The spec then proceeds to do a simple for loop over length items, making a list of corresponding values (list is some internal voodoo, but it's basically an array). In terms of very, very loose code:
Function.prototype.apply = function (thisArg, argArray) {
var len = argArray.length,
argList = [];
for (var i = 0; i < len; i += 1) {
argList[i] = argArray[i];
}
//yeah...
superMagicalFunctionInvocation(this, thisArg, argList);
};
So all we need to mimic an argArray in this case is an object with a length property. And now we can see why the values are undefined, but the keys aren't, on arguments: We create the key=>value mappings.
Phew, so this might not have been shorter than the previous part. But there'll be cake when we finish, so be patient! However, after the following section (which'll be short, I promise) we can begin dissecting the expression. In case you forgot, the question was how does the following work:
Array.apply(null, { length: 5 }).map(Number.call, Number);
3. How Array handles multiple arguments
So! We saw what happens when you pass a length argument to Array, but in the expression, we pass several things as arguments (an array of 5 undefined, to be exact). Section 15.4.2.1 tells us what to do. The last paragraph is all that matters to us, and it's worded really oddly, but it kind of boils down to:
function Array () {
var ret = [];
ret.length = arguments.length;
for (var i = 0; i < arguments.length; i += 1) {
ret[i] = arguments[i];
}
return ret;
}
Array(0, 1, 2); //[0, 1, 2]
Array.apply(null, [0, 1, 2]); //[0, 1, 2]
Array.apply(null, Array(2)); //[undefined, undefined]
Array.apply(null, {length:2}); //[undefined, undefined]
Tada! We get an array of several undefined values, and we return an array of these undefined values.
The first part of the expression
Finally, we can decipher the following:
Array.apply(null, { length: 5 })
We saw that it returns an array containing 5 undefined values, with keys all in existence.
Now, to the second part of the expression:
[undefined, undefined, undefined, undefined, undefined].map(Number.call, Number)
This will be the easier, non-convoluted part, as it doesn't so much rely on obscure hacks.
4. How Number treats input
Doing Number(something) (section 15.7.1) converts something to a number, and that is all. How it does that is a bit convoluted, especially in the cases of strings, but the operation is defined in section 9.3 in case you're interested.
5. Games of Function.prototype.call
call is apply's brother, defined in section 15.3.4.4. Instead of taking an array of arguments, it just takes the arguments it received, and passes them forward.
Things get interesting when you chain more than one call together, crank the weird up to 11:
function log () {
console.log(this, arguments);
}
log.call.call(log, {a:4}, {a:5});
//{a:4}, [{a:5}]
//^---^ ^-----^
// this arguments
This is quite wtf worthy until you grasp what's going on. log.call is just a function, equivalent to any other function's call method, and as such, has a call method on itself as well:
log.call === log.call.call; //true
log.call === Function.call; //true
And what does call do? It accepts a thisArg and a bunch of arguments, and calls its parent function. We can define it via apply (again, very loose code, won't work):
Function.prototype.call = function (thisArg) {
var args = arguments.slice(1); //I wish that'd work
return this.apply(thisArg, args);
};
Let's track how this goes down:
log.call.call(log, {a:4}, {a:5});
this = log.call
thisArg = log
args = [{a:4}, {a:5}]
log.call.apply(log, [{a:4}, {a:5}])
log.call({a:4}, {a:5})
this = log
thisArg = {a:4}
args = [{a:5}]
log.apply({a:4}, [{a:5}])
The later part, or the .map of it all
It's not over yet. Let's see what happens when you supply a function to most array methods:
function log () {
console.log(this, arguments);
}
var arr = ['a', 'b', 'c'];
arr.forEach(log);
//window, ['a', 0, ['a', 'b', 'c']]
//window, ['b', 1, ['a', 'b', 'c']]
//window, ['c', 2, ['a', 'b', 'c']]
//^----^ ^-----------------------^
// this arguments
If we don't provide a this argument ourselves, it defaults to window. Take note of the order in which the arguments are provided to our callback, and let's weird it up all the way to 11 again:
arr.forEach(log.call, log);
//'a', [0, ['a', 'b', 'c']]
//'b', [1, ['a', 'b', 'c']]
//'b', [2, ['a', 'b', 'c']]
// ^ ^
Whoa whoa whoa...let's back up a bit. What's going on here? We can see in section 15.4.4.18, where forEach is defined, the following pretty much happens:
var callback = log.call,
thisArg = log;
for (var i = 0; i < arr.length; i += 1) {
callback.call(thisArg, arr[i], i, arr);
}
So, we get this:
log.call.call(log, arr[i], i, arr);
//After one `.call`, it cascades to:
log.call(arr[i], i, arr);
//Further cascading to:
log(i, arr);
Now we can see how .map(Number.call, Number) works:
Number.call.call(Number, arr[i], i, arr);
Number.call(arr[i], i, arr);
Number(i, arr);
Which returns the transformation of i, the current index, to a number.
In conclusion,
The expression
Array.apply(null, { length: 5 }).map(Number.call, Number);
Works in two parts:
var arr = Array.apply(null, { length: 5 }); //1
arr.map(Number.call, Number); //2
The first part creates an array of 5 undefined items. The second goes over that array and takes its indices, resulting in an array of element indices:
[0, 1, 2, 3, 4]
Disclaimer: This is a very formal description of the above code - this is how I know how to explain it. For a simpler answer - check Zirak's great answer above. This is a more in depth specification in your face and less "aha".
Several things are happening here. Let's break it up a bit.
var arr = Array.apply(null, { length: 5 }); // Create an array of 5 `undefined` values
arr.map(Number.call, Number); // Calculate and return a number based on the index passed
In the first line, the array constructor is called as a function with Function.prototype.apply.
The this value is null which does not matter for the Array constructor (this is the same this as in the context according to 15.3.4.3.2.a.
Then new Array is called being passed an object with a length property - that causes that object to be an array like for all it matters to .apply because of the following clause in .apply:
Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
As such, .apply is passing arguments from 0 to .length , since calling [[Get]] on { length: 5 } with the values 0 to 4 yields undefined the array constructor is called with five arguments whose value is undefined (getting an undeclared property of an object).
The array constructor is called with 0, 2 or more arguments.
The length property of the newly constructed array is set to the number of arguments according to the specification and the values to the same values.
Thus var arr = Array.apply(null, { length: 5 }); creates a list of five undefined values.
Note: Notice the difference here between Array.apply(0,{length: 5}) and Array(5), the first creating five times the primitive value type undefined and the latter creating an empty array of length 5. Specifically, because of .map's behavior (8.b) and specifically [[HasProperty] .
So the code above in a compliant specification is the same as:
var arr = [undefined, undefined, undefined, undefined, undefined];
arr.map(Number.call, Number); // Calculate and return a number based on the index passed
Now off to the second part.
Array.prototype.map calls the callback function (in this case Number.call) on each element of the array and uses the specified this value (in this case setting the this value to `Number).
The second parameter of the callback in map (in this case Number.call) is the index, and the first is the this value.
This means that Number is called with this as undefined (the array value) and the index as the parameter. So it's basically the same as mapping each undefined to its array index (since calling Number performs type conversion, in this case from number to number not changing the index).
Thus, the code above takes the five undefined values and maps each to its index in the array.
Which is why we get the result to our code.
As you said, the first part:
var arr = Array.apply(null, { length: 5 });
creates an array of 5 undefined values.
The second part is calling the map function of the array which takes 2 arguments and returns a new array of the same size.
The first argument which map takes is actually a function to apply on each element in the array, it is expected to be a function which takes 3 arguments and returns a value.
For example:
function foo(a,b,c){
...
return ...
}
if we pass the function foo as the first argument it will be called for each element with
a as the value of the current iterated element
b as the index of the current iterated element
c as the whole original array
The second argument which map takes is being passed to the function which you pass as the first argument. But it would not be a, b, nor c in case of foo, it would be this.
Two examples:
function bar(a,b,c){
return this
}
var arr2 = [3,4,5]
var newArr2 = arr2.map(bar, 9);
//newArr2 is equal to [9,9,9]
function baz(a,b,c){
return b
}
var newArr3 = arr2.map(baz,9);
//newArr3 is equal to [0,1,2]
and another one just to make it clearer:
function qux(a,b,c){
return a
}
var newArr4 = arr2.map(qux,9);
//newArr4 is equal to [3,4,5]
So what about Number.call ?
Number.call is a function that takes 2 arguments, and tries to parse the second argument to a number (I'm not sure what it does with the first argument).
Since the second argument that map is passing is the index, the value that will be placed in the new array at that index is equal to the index. Just like the function baz in the example above. Number.call will try to parse the index - it will naturally return the same value.
The second argument you passed to the map function in your code doesn't actually have an effect on the result. Correct me if I'm wrong, please.
An array is simply an object comprising the 'length' field and some methods (e.g. push). So arr in var arr = { length: 5} is basically the same as an array where the fields 0..4 have the default value which is undefined (i.e. arr[0] === undefined yields true).
As for the second part, map, as the name implies, maps from one array to a new one. It does so by traversing through the original array and invoking the mapping-function on each item.
All that's left is to convince you that the result of mapping-function is the index. The trick is to use the method named 'call'(*) which invokes a function with the small exception that the first param is set to be the 'this' context, and the second becomes the first param (and so on). Coincidentally, when the mapping-function is invoked, the second param is the index.
Last but not least, the method which is invoked is the Number "Class", and as we know in JS, a "Class" is simply a function, and this one (Number) expects the first param to be the value.
(*) found in Function's prototype (and Number is a function).
MASHAL
I am looking for a JavaScript array insert method, in the style of:
arr.insert(index, item)
Preferably in jQuery, but any JavaScript implementation will do at this point.
You want the splice function on the native array object.
arr.splice(index, 0, item); will insert item into arr at the specified index (deleting 0 items first, that is, it's just an insert).
In this example we will create an array and add an element to it into index 2:
var arr = [];
arr[0] = "Jani";
arr[1] = "Hege";
arr[2] = "Stale";
arr[3] = "Kai Jim";
arr[4] = "Borge";
console.log(arr.join()); // Jani,Hege,Stale,Kai Jim,Borge
arr.splice(2, 0, "Lene");
console.log(arr.join()); // Jani,Hege,Lene,Stale,Kai Jim,Borge
You can implement the Array.insert method by doing this:
Array.prototype.insert = function ( index, ...items ) {
this.splice( index, 0, ...items );
};
Then you can use it like:
var arr = [ 'A', 'B', 'E' ];
arr.insert(2, 'C', 'D');
// => arr == [ 'A', 'B', 'C', 'D', 'E' ]
Other than splice, you can use this approach which will not mutate the original array, but it will create a new array with the added item. It is useful, when you need to avoid mutation. I'm using the ES6 spread operator here.
const items = [1, 2, 3, 4, 5]
const insert = (arr, index, newItem) => [
// part of the array before the specified index
...arr.slice(0, index),
// inserted item
newItem,
// part of the array after the specified index
...arr.slice(index)
]
const result = insert(items, 1, 10)
console.log(result)
// [1, 10, 2, 3, 4, 5]
This can be used to add more than one item by tweaking the function a bit to use the rest operator for the new items, and spread that in the returned result as well:
const items = [1, 2, 3, 4, 5]
const insert = (arr, index, ...newItems) => [
// part of the array before the specified index
...arr.slice(0, index),
// inserted items
...newItems,
// part of the array after the specified index
...arr.slice(index)
]
const result = insert(items, 1, 10, 20)
console.log(result)
// [1, 10, 20, 2, 3, 4, 5]
Custom array insert methods
1. With multiple arguments and chaining support
/* Syntax:
array.insert(index, value1, value2, ..., valueN) */
Array.prototype.insert = function(index) {
this.splice.apply(this, [index, 0].concat(
Array.prototype.slice.call(arguments, 1)));
return this;
};
It can insert multiple elements (as native splice does) and supports chaining:
["a", "b", "c", "d"].insert(2, "X", "Y", "Z").slice(1, 6);
// ["b", "X", "Y", "Z", "c"]
2. With array-type arguments merging and chaining support
/* Syntax:
array.insert(index, value1, value2, ..., valueN) */
Array.prototype.insert = function(index) {
index = Math.min(index, this.length);
arguments.length > 1
&& this.splice.apply(this, [index, 0].concat([].pop.call(arguments)))
&& this.insert.apply(this, arguments);
return this;
};
It can merge arrays from the arguments with the given array and also supports chaining:
["a", "b", "c", "d"].insert(2, "V", ["W", "X", "Y"], "Z").join("-");
// "a-b-V-W-X-Y-Z-c-d"
DEMO: http://jsfiddle.net/UPphH/
Using Array.prototype.splice() is an easy way to achieve it
const numbers = ['one', 'two', 'four', 'five']
numbers.splice(2, 0, 'three');
console.log(numbers)
Read more about Array.prototype.splice
If you want to insert multiple elements into an array at once check out this Stack Overflow answer: A better way to splice an array into an array in javascript
Also here are some functions to illustrate both examples:
function insertAt(array, index) {
var arrayToInsert = Array.prototype.splice.apply(arguments, [2]);
return insertArrayAt(array, index, arrayToInsert);
}
function insertArrayAt(array, index, arrayToInsert) {
Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
return array;
}
Finally here is a jsFiddle so you can see it for yourself: http://jsfiddle.net/luisperezphd/Wc8aS/
And this is how you use the functions:
// if you want to insert specific values whether constants or variables:
insertAt(arr, 1, "x", "y", "z");
// OR if you have an array:
var arrToInsert = ["x", "y", "z"];
insertArrayAt(arr, 1, arrToInsert);
Solutions & Performance
Today (2020.04.24) I perform tests for chosen solutions for big and small arrays. I tested them on macOS v10.13.6 (High Sierra) on Chrome 81.0, Safari 13.1, and Firefox 75.0.
Conclusions
For all browsers
surprisingly for small arrays, non-in-place solutions based on slice and reduce (D,E,F) are usually 10x-100x faster than in-place solutions
for big arrays the in-place-solutions based on splice (AI, BI, and CI) was fastest (sometimes ~100x - but it depends on the array size)
for small arrays the BI solution was slowest
for big arrays the E solution was slowest
Details
Tests were divided into two groups: in-place solutions (AI, BI, and CI) and non-in-place solutions (D, E, and F) and was performed for two cases:
test for an array with 10 elements - you can run it here
test for an array with 1,000,000 elements - you can run it here
Tested code is presented in the below snippet:
jsfiddle
function AI(arr, i, el) {
arr.splice(i, 0, el);
return arr;
}
function BI(arr, i, el) {
Array.prototype.splice.apply(arr, [i, 0, el]);
return arr;
}
function CI(arr, i, el) {
Array.prototype.splice.call(arr, i, 0, el);
return arr;
}
function D(arr, i, el) {
return arr.slice(0, i).concat(el, arr.slice(i));
}
function E(arr, i, el) {
return [...arr.slice(0, i), el, ...arr.slice(i)]
}
function F(arr, i, el) {
return arr.reduce((s, a, j)=> (j-i ? s.push(a) : s.push(el, a), s), []);
}
// -------------
// TEST
// -------------
let arr = ["a", "b", "c", "d", "e", "f"];
let log = (n, f) => {
let a = f([...arr], 3, "NEW");
console.log(`${n}: [${a}]`);
};
log('AI', AI);
log('BI', BI);
log('CI', CI);
log('D', D);
log('E', E);
log('F', F);
This snippet only presents tested code (it not perform tests)
Example results for a small array on Google Chrome are below:
For proper functional programming and chaining purposes, an invention of Array.prototype.insert() is essential. Actually, the splice could have been perfect if it had returned the mutated array instead of a totally meaningless empty array. So here it goes:
Array.prototype.insert = function(i,...rest){
this.splice(i,0,...rest)
return this
}
var a = [3,4,8,9];
document.write("<pre>" + JSON.stringify(a.insert(2,5,6,7)) + "</pre>");
Well, OK, the above with the Array.prototype.splice() one mutates the original array and some might complain like "you shouldn't modify what doesn't belong to you" and that might turn out to be right as well. So for the public welfare, I would like to give another Array.prototype.insert() which doesn't mutate the original array. Here it goes;
Array.prototype.insert = function(i,...rest){
return this.slice(0,i).concat(rest,this.slice(i));
}
var a = [3,4,8,9],
b = a.insert(2,5,6,7);
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
You can use splice() for this
The splice() method usually receives three arguments when adding an element:
The index of the array where the item is going to be added.
The number of items to be removed, which in this case is 0.
The element to add.
let array = ['item 1', 'item 2', 'item 3']
let insertAtIndex = 0
let itemsToRemove = 0
array.splice(insertAtIndex, itemsToRemove, 'insert this string on index 0')
console.log(array)
I recommend using pure JavaScript in this case. Also there isn't any insert method in JavaScript, but we have a method which is a built-in Array method which does the job for you. It's called splice...
Let's see what's splice()...
The splice() method changes the contents of an array by removing
existing elements and/or adding new elements.
OK, imagine we have this array below:
const arr = [1, 2, 3, 4, 5];
We can remove 3 like this:
arr.splice(arr.indexOf(3), 1);
It will return 3, but if we check the arr now, we have:
[1, 2, 4, 5]
So far, so good, but how we can add a new element to array using splice?
Let's put back 3 in the arr...
arr.splice(2, 0, 3);
Let's see what we have done...
We use splice again, but this time for the second argument, we pass 0, meaning we don't want to delete any item, but at the same time, we add a third argument which is the 3 that will be added at second index...
You should be aware that we can delete and add at the same time. For example, now we can do:
arr.splice(2, 2, 3);
Which will delete two items at index 2. Then add 3 at index 2 and the result will be:
[1, 2, 3, 5];
This is showing how each item in splice work:
array.splice(start, deleteCount, item1, item2, item3 ...)
Here are two ways:
const array = [ 'My', 'name', 'Hamza' ];
array.splice(2, 0, 'is');
console.log("Method 1: ", array.join(" "));
Or
Array.prototype.insert = function ( index, item ) {
this.splice( index, 0, item );
};
const array = [ 'My', 'name', 'Hamza' ];
array.insert(2, 'is');
console.log("Method 2 : ", array.join(" "));
Append a single element at a specific index
// Append at a specific position (here at index 1)
arrName.splice(1, 0,'newName1');
// 1: index number, 0: number of element to remove, newName1: new element
// Append at a specific position (here at index 3)
arrName[3] = 'newName1';
Append multiple elements at a specific index
// Append from index number 1
arrName.splice(1, 0, 'newElemenet1', 'newElemenet2', 'newElemenet3');
// 1: index number from where append start,
// 0: number of element to remove,
//newElemenet1,2,3: new elements
Array#splice() is the way to go, unless you really want to avoid mutating the array. Given 2 arrays arr1 and arr2, here's how you would insert the contents of arr2 into arr1 after the first element:
const arr1 = ['a', 'd', 'e'];
const arr2 = ['b', 'c'];
arr1.splice(1, 0, ...arr2); // arr1 now contains ['a', 'b', 'c', 'd', 'e']
console.log(arr1)
If you are concerned about mutating the array (for example, if using Immutable.js), you can instead use slice(), not to be confused with splice() with a 'p'.
const arr3 = [...arr1.slice(0, 1), ...arr2, ...arr1.slice(1)];
Another possible solution, with usage of Array.reduce.
const arr = ["apple", "orange", "raspberry"];
const arr2 = [1, 2, 4];
const insert = (arr, item, index) =>
arr.reduce(function(s, a, i) {
i === index ? s.push(item, a) : s.push(a);
return s;
}, []);
console.log(insert(arr, "banana", 1));
console.log(insert(arr2, 3, 2))
Even though this has been answered already, I'm adding this note for an alternative approach.
I wanted to place a known number of items into an array, into specific positions, as they come off of an "associative array" (i.e. an object) which by definition is not guaranteed to be in a sorted order. I wanted the resulting array to be an array of objects, but the objects to be in a specific order in the array since an array guarantees their order. So I did this.
First the source object, a JSONB string retrieved from PostgreSQL. I wanted to have it sorted by the "order" property in each child object.
var jsonb_str = '{"one": {"abbr": "", "order": 3}, "two": {"abbr": "", "order": 4}, "three": {"abbr": "", "order": 5}, "initialize": {"abbr": "init", "order": 1}, "start": {"abbr": "", "order": 2}}';
var jsonb_obj = JSON.parse(jsonb_str);
Since the number of nodes in the object is known, I first create an array with the specified length:
var obj_length = Object.keys(jsonb_obj).length;
var sorted_array = new Array(obj_length);
And then iterate the object, placing the newly created temporary objects into the desired locations in the array without really any "sorting" taking place.
for (var key of Object.keys(jsonb_obj)) {
var tobj = {};
tobj[key] = jsonb_obj[key].abbr;
var position = jsonb_obj[key].order - 1;
sorted_array[position] = tobj;
}
console.dir(sorted_array);
Immutable insertion
Using the splice method is surely the best answer if you need to insert into an array in-place.
However, if you are looking for an immutable function that returns a new updated array instead of mutating the original array on insert, you can use the following function.
function insert(array, index) {
const items = Array.prototype.slice.call(arguments, 2);
return [].concat(array.slice(0, index), items, array.slice(index));
}
const list = ['one', 'two', 'three'];
const list1 = insert(list, 0, 'zero'); // Insert single item
const list2 = insert(list, 3, 'four', 'five', 'six'); // Insert multiple
console.log('Original list: ', list);
console.log('Inserted list1: ', list1);
console.log('Inserted list2: ', list2);
Note: This is a pre-ES6 way of doing it, so it works for both older and newer browsers.
If you're using ES6 then you can try out rest parameters too; see this answer.
Anyone who's still having issues with this one and have tried all the options in previous answers and never got it. I'm sharing my solution, and this is to take into consideration that you don't want to explicitly state the properties of your object vs the array.
function isIdentical(left, right){
return JSON.stringify(left) === JSON.stringify(right);
}
function contains(array, obj){
let count = 0;
array.map((cur) => {
if(this.isIdentical(cur, obj))
count++;
});
return count > 0;
}
This is a combination of iterating the reference array and comparing it to the object you wanted to check, converting both of them into a string, and then iterating if it matched. Then you can just count. This can be improved, but this is where I settled.
Taking profit of the reduce method as follows:
function insert(arr, val, index) {
return index >= arr.length
? arr.concat(val)
: arr.reduce((prev, x, i) => prev.concat(i === index ? [val, x] : x), []);
}
So in this way we can return a new array (will be a cool functional way - more much better than using push or splice) with the element inserted at index, and if the index is greater than the length of the array it will be inserted at the end.
I tried this and it is working fine!
var initialArr = ["India","China","Japan","USA"];
initialArr.splice(index, 0, item);
Index is the position where you want to insert or delete the element.
0, i.e., the second parameter, defines the number of elements from the index to be removed.
item contains the new entries which you want to make in the array. It can be one or more than one.
initialArr.splice(2, 0, "Nigeria");
initialArr.splice(2, 0, "Australia","UK");
I have to agree with Redu's answer because splice() definitely has a bit of a confusing interface. And the response given by cdbajorin that "it only returns an empty array when the second parameter is 0. If it's greater than 0, it returns the items removed from the array" is, while accurate, proving the point.
The function's intent is to splice or as said earlier by Jakob Keller, "to join or connect, also to change.
You have an established array that you are now changing which would involve adding or removing elements...." Given that, the return value of the elements, if any, that were removed is awkward at best. And I 100% agree that this method could have been better suited to chaining if it had returned what seems natural, a new array with the spliced elements added. Then you could do things like ["19", "17"].splice(1,0,"18").join("...") or whatever you like with the returned array.
The fact that it returns what was removed is just kind of nonsense IMHO. If the intention of the method was to "cut out a set of elements" and that was its only intent, maybe. It seems like if I don't know what I'm cutting out already though, I probably have little reason to cut those elements out, doesn't it?
It would be better if it behaved like concat(), map(), reduce(), slice(), etc. where a new array is made from the existing array rather than mutating the existing array. Those are all chainable, and that is a significant issue. It's rather common to chain array manipulation.
It seems like the language needs to go one or the other direction and try to stick to it as much as possible. JavaScript being functional and less declarative, it just seems like a strange deviation from the norm.
I like a little safety and I use this:
Array.prototype.Insert = function (item, before) {
if (!item) return;
if (before == null || before < 0 || before > this.length - 1) {
this.push(item);
return;
}
this.splice(before, 0, item);
}
var t = ["a", "b"]
t.Insert("v", 1)
console.log(t)
You can do it with array.splice:
/**
* #param arr: Array
* #param item: item to insert
* #param index: index at which to insert
* #returns array with the inserted element
*/
export function _arrayInsertAt<T>(arr: T[], item: T, index: number) {
return arr.splice(index, 0, item);;
}
Doc of array.slice
Here's a working function that I use in one of my applications.
This checks if an item exists:
let ifExist = (item, strings = [ '' ], position = 0) => {
// Output into an array with an empty string. Important just in case their isn't any item.
let output = [ '' ];
// Check to see if the item that will be positioned exist.
if (item) {
// Output should be equal to an array of strings.
output = strings;
// Use splice() in order to break the array.
// Use positional parameters to state where to put the item
// and 0 is to not replace an index. Item is the actual item we are placing at the prescribed position.
output.splice(position, 0, item);
}
// Empty string is so we do not concatenate with comma or anything else.
return output.join("");
};
And then I call it below.
ifExist("friends", [ ' ( ', ' )' ], 1)} // Output: ( friends )
ifExist("friends", [ ' - '], 1)} // Output: - friends
ifExist("friends", [ ':'], 0)} // Output: friends:
Here is the modern (Typescript functional) way:
export const insertItemInList = <T>(
arr: T[],
index: number,
newItem: T
): T[] => [...arr.slice(0, index), newItem, ...arr.slice(index)]
I do it like so:
const insert = (what, where, index) =>
([...where.slice(0, index), what , ...where.slice(index, where.length)]);
const insert = (what, where, index) =>
([...where.slice(0, index), what , ...where.slice(index, where.length)]);
const list = [1, 2, 3, 4, 5, 6];
const newList = insert('a', list, 2);
console.log(newList.indexOf('a') === 2);
Here's a simple function that supports inserting multiple values at the same time:
function add_items_to_array_at_position(array, index, new_items)
{
return [...array.slice(0, index), ...new_items, ...array.slice(index)];
}
Usage example:
let old_array = [1,2,5];
let new_array = add_items_to_array_at_position(old_array, 2, [3,4]);
console.log(new_array);
//Output: [1,2,3,4,5]
var array= [10,20,30,40]
var i;
var pos=2; //pos=index + 1
/*pos is position which we want to insert at which is index + 1.position two in an array is index 1.*/
var value=5
//value to insert
//Initialize from last array element
for(i=array.length-1;i>=pos-1;i--){
array[i+1]=array[i]
}
array[pos-1]=value
console.log(array)
Multi purpose for ARRAY and ARRAY OF OBJECT reusable approach
let arr = [0,1,2];
let obj = [{ name: "abc"},{ name: "xyz"},{ name: "ijk"} ];
const addArrayItemAtIndex = ( array, index, newItem ) => {
return [...array.slice(0, index), newItem, ...array.slice(index)];
}
// For Array
console.log( addArrayItemAtIndex(arr, 2, 159 ) );
// For Array of Objects
console.log( addArrayItemAtIndex(obj, 0, { name: "AMOOS"} ) );