Javascript reduce accumulator explained - javascript
Who can kindly explain the accumulator acc construction below in plain English?
return arr1.reduce(function(acc, curr){
var last = acc[acc.length-1];
if(acc.length > 0 && curr[1]===last[1]) {
last[0] += curr[0];
} else acc.push(curr);
return acc;
}, []);
}
This reduce method can be used to solve the FreeCodeCamp "Inventory Update" assignment, as part of their advanced algorithm scripting lessons.
One is required to update existing items in an inventory array (arr1) with new items in a "new delivery" array.
Two test arrays, a present inventory curInv and a new delivery newInv respectively, could be as follows:
var curInv = [
[21, "Bowling Ball"],
[2, "Dirty Sock"],
[1, "Hair Pin"],
[5, "Microphone"]
];
var newInv = [
[2, "Hair Pin"],
[3, "Half-Eaten Apple"],
[67, "Bowling Ball"],
[7, "Toothpaste"]
];
After finding several excellent articles on Javascript reduce method (such this post and a great video course on egghead.io), and somehow sensing the power it is harnassing, I would read the method as follows:
Reduce the inventory array, by creating an empty array [ ] first (the initial value), then applying the following callback function:
If the inventory array is currently not empty (has a length greater than zero), and the name of currently handled item (index 0 of curr could read "Bowling Ball" for example) is identical to the last item of the inventory array being updated, then update the amount of this item in the inventory array.
The last item is defined right above the if statement, as follows: take the present length of the accumulated array upto now, subtract 1, and use this value to index the accumulated array. The element at that index is then assigned to the variable 'last'.
On the other hand, if the inventory is empty, add the new item entirely, - that is: item name and amount.
Now return the newly accumulated array."*
How is using the length - 1 of the accumulator useful to make acc actually accumulate?
(pardon the alliteration)
I think I understand most of how this reduce method is built, but please correct me wherever I'm misreading), except this particular
use of acc.length-1.
Cheers, k.
The actual solution involves concatenating and sorting both arrays and only then reducing them.
In this case whenever we evaluate a new item, if its name is not equal to the last accumulator item, it means it's a new item.
Using your example, the list we are reducing is:
[
[ 21, 'Bowling Ball' ],
[ 67, 'Bowling Ball' ],
[ 2, 'Dirty Sock' ],
[ 1, 'Hair Pin' ],
[ 2, 'Hair Pin' ],
[ 3, 'Half-Eaten Apple' ],
[ 5, 'Microphone' ],
[ 7, 'Toothpaste' ]
]
so when we encounter the second item, the last value in the accumulator is [21, 'Boweling Ball'] and when we compare the strings we go into the first condition.
Are you asking about this section?
var last = acc[acc.length-1];
If so acc.length-1 is so because in an array
acc = [a,b,c,d]
acc.length is equal to 4
to access element d you will access it via
acc[3]; //which equals d
That is because we count positions from 0,1,2,3
tl;dr: Check out this REPL example
Reduce is capable of reducing the original array to a value that is defined on the function.
For instance, if we had an array of integers that we wanted to compute the total sum of it, we could use reduce to achieve that.
Of course, you might ask why would you ever want to use reduce as opposed to a for loop?
The primary reason why reduce is a better choice is because the reducer function that we pass as a parameter is pure. That means, there are no 'extra' nor 'external' variables that are needed in order to compute the result. In a for loop, we would need to have a totalSum variable to compute the result, whereas in the reduce version this is not needed.
Back to the OP's example, this is how we could construct the reduce to group by the invites by the same names:
// reduce example
const reducer = (accumulator, currentValue) => {
if (accumulator.length === 0) {
accumulator.push(currentValue);
return accumulator;
}
const index = accumulator.findIndex(el => el[1] === currentValue[1]);
if (index !== -1) {
accumulator[index][0] += currentValue[0];
return accumulator;
}
accumulator.push(currentValue);
return accumulator
};
Here's the equivalent version with the for loop:
// for loop example
let withForLoop = (input) => {
let res = [];
for (let i = 0; i < input.length; i++) {
let currInput = input[i];
const index = res.findIndex(el => el[1] === currInput[1]);
if (index !== -1) {
res[index][0] += currInput[0];
continue;
}
res.push(currInput);
}
return res;
}
If you are new to reduce, it might take some time until you completely understand it. Its usage almost always leads to less bug-prone applications, thus I would advocate for taking some time to fully understand its capabilities and when to use it.
Related
Problem with getting javascript to forget a variables value
I'm trying to solve a coding challenge it gives me an array and waits for the answer ,then gives me the next array and so on. https://www.codewars.com/kata/5648b12ce68d9daa6b000099/train/javascript I am trying to take this input: var busStops = [[10,0],[3,5],[5,8]] and return 5. the code is supposed to add the first in each pair to a total while subtracting the second in each pair from the total eg: 10 - 0 + 3 - 5 + 5 - 8 = 5 First my code loops through the inner arrays and outer array ,pushing it into myarr as a regular array eg: [10,0,3,5,5,8]. It then adds the value if it is index is 0 or even and subtracts it if the index is odd. This actually works! Until it is given a second array eg: [[3,0],[9,1],[4,10],[12,2],[6,1],[7,10]] It is still calculating the total correctly but is still remembering the total from the first array meaning it is returning 22 instead of 17 Why? There is a var answer = 0 that is being executed ahead of the second loop It should forget the value of the previous answer. Right? Edit: I figured out my problem. I just needed to empty myarr after the total was calculated! let myarr = []; var number = function (busStops) { for (var i = 0; i < busStops.length; i++) { for (var j = 0; j < busStops[i].length; j++) { /*console.log(busStops[i][j]);*/ myarr.push(busStops[i][j]); } } console.log("test"); var answer = 0; console.log("test again"); for (let t = 0; t < myarr.length; t++) { if (t == 0 || t % 2 == 0) { answer = answer + myarr[t]; } else { answer = answer - myarr[t]; } } console.log(answer); return answer; };
The task at your hand tries to somehow find a value (an integer) from an array of arrays (multidimensional array). That task seems to be reducing that multidimensional array into a single integer. Luckily, JavaScript has some powerful array methods and one of them is the reduce method: The reduce() method executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value. Source: MDN The reduce method tries to reduce an array's elements into a single value and in your case we want to reduce that multidimensional array into a single value that is the number persons who are still in the bus. Before typing some code, let's dig a bit deeper into the reduce method: It accepts 2 parameters, a callback function that acts as the reducer and the initial value to be used in the first iteration of the reduce method. The reducer callback function, on its own, accepts 4 parameters that are supplied by the reduce method. You may learn more about those parameters here as am only going to focus on the first 2 parameters the reducer accepts: previousValue: hold the value from the previous reducer call. On first call, it contains the value you set to the initial value parameter of the reduce method or, if you didn't supply an initial value, previousValue shall hold the value of your array's first element (arr[0]). currentValue: hold the current reduce iteration's item. Now, let's get back to the task we have, we need to calculate the number of persons who are still in the bus based on a supplied multidimensional array. Each item in that multidimensional array is an array of two values where the result we need at the end is: the sum of the differences between each array, in the multidimensional array, first and second values (sum = multiDim[i][0] - multiDim[i][1] + multiDim[i + 1][0] + multiDim[i + 1][1] etc...). To solve this task, we'll reduce that multidimensional array into a single number (let's call it result) by using a simple reducer function that will start by an initial value of 0 (as we're calculating a sum in our case) and will add, to the result, the difference between the first and the second values of the array supplied by the reduce at each iteration. To illustrate, here's a live demo: /** * a function that calculates and returns the number of person who are still in the bus or precisely, the sum of the differences between "c[0]" and "c[1]" * busArray: the supplied multidimensional array to reduce * the reducer accepts two parameters: * r: the result from the last call to the reducer function (initially set to 0, the initial value (see second parameter passed to the "reduce" method)) c: hold the current iteration's array. */ const calculateWhoAreInTheBus = busArray => busArray.reduce((r, c) => r += c[0] - c[1], 0); /** test the created "calculateWhoAreInTheBus" function */ console.log(calculateWhoAreInTheBus([ [10, 0], [3, 5], [5, 8] ])); // should print: 5 console.log(calculateWhoAreInTheBus([ [3, 0], [9, 1], [4, 10], [12, 2], [6, 1], [7, 10] ])); // should print: 17 console.log(calculateWhoAreInTheBus([ [3, 0], [9, 1], [4, 8], [12, 2], [6, 1], [7, 8] ])); // should print: 21 console.log(calculateWhoAreInTheBus([ [0, 0], [0, 0] ])); // should print: 0
I would advice you to use Array.prototype.reduce instead. For example like this: const reducer = (previous, current) => previous + current[0] - current[1]; const answer = busStops.reduce(reducer, 0); It is very brief (although this is not a goal in and of itself) and the reducer function does almost trivial work, so it does not complicate unneccesarily. Best of all it encapsulates the functionality with a minimal need of extra variables. Othwerwise you could simplify your function a bit but use the let keyword to keep variables locked to scope like: function number(busStops) { let answer = 0; for (let bs of busStops) { answer += bs[0] - bs[1]; } return answer; }
Sum of two multi dimensional array
I have a array in JavaScript like this. var arr= [ ['A'],[1,2,3,4], ['A'],[4,3,2,1], ['B'],[10,12,3,1], ['B'],[1,2,3,4], . . . . ['AZ'],[1,2,3,4] ] and I want the output to summarize the array like - var output= [ ['A'],[5,5,5,5], ['B'],[11,14,6,5], ['AZ'],[1,2,3,4] ] Thanks.
Script You can use the following script to achieve what you want to do. const arr = [ ["A"], [1, 2, 3, 4], ["A"], [4, 3, 2, 1], ["B"], [10, 12, 3, 1], ["B"], [1, 2, 3, 4], ["AZ"], [1, 2, 3, 4], ]; /** * Generator to return key-value pairs with array[i] being the key and array[i+1] being the value * #param {Array<any>} array */ function* keyValue(array) { // make sure we can build pairs (other ways of handling this are also possible) if (array.length % 2 !== 0) throw new RangeError( "Array length must be dividable by 2 without remainder!" ); for (let i = 0; i < array.length; i += 2) { yield [array[i], array[i + 1]]; } } // here the created key-value pairs console.log("Key-value pairs created by keyValue() generator function:"); console.log([...keyValue(arr)]); // loop over key value pairs and sum up all the individul arrays based on the letter assigned to them const result = [...keyValue(arr)].reduce((all, [[key], array]) => { // if we don't have values for this letter, assing copy of the array to that letter if (!all[key]) all[key] = [...array]; // we have some values for that letter already, sum up each value else all[key] = all[key].map((prev, idx) => prev + array[idx]); return all; }, {}); // this would be a "better" result to my mind as there is no point wrapping single string values in arrays // When using objects the values can easily be accessed in O(1) console.log(result); // now transform JS object to array of arrays console.log("Result:"); const transformed = Object.entries(result).flatMap(([key, value]) => [[key], value]); console.log(transformed); .as-console-wrapper { max-height: 100% !important; top: 0; } Please note: This implementation assumes that the arrays for a given letter have the same length (as is the case in your example). Explanation First of all, I use a generator function keyValue() to always group two consecutive values in the array (a key and a value) together. One could also do this differently but once you understand how generators work that's an easy and elegant approach, I think. For this demo I just throw an error if the array is not dividable by 2 without remainder, but one could also handle this more gracefully. Then, using reduce(), I iterate over the array created by using keyValue() and for each element in the array I check if I've encountered that value before. If I have not, I create a copy of the array (for immutablility) and assign it to the key i.e. a letter. If I have encountered a certain letter before I add up the values that I have previously saved assigned to that letter with the ones I am currently processing. After iteration all sums are calculated and I have a JavaScript object containing the results. To my mind, this would be a good output because your output is a bit odd, since there is no point storing single letters in an array or even arrays of arrays. Using a JavaScript object is much more convenient and faster for lookups. Nevertheless, you can easily deduct your result from the created object using flatMap().
Why map or reduce keeps running without any condition given?
const array = [7, 2, 4, 1, 10, 6, 5, 11] const max = array.reduce((acc, val) => { console.log(val, acc) return val > acc ? val : acc }, 0) console.log(max) I was looking at this code of reduce array method, one thing I couldn't understand at all is, How the reducer function is going to the next iteration? There is no condition that forces the reducer function to go to the next element in the array. In the first iteration, the val is 7, the first element of the array, and acc is 0, the reducer function returns 7 as per the condition written. My question is how the number 7 as being the new accumulator is going to be called on the reducer function. I thought the normal procedure is you have to meet some kind of condition to iterate over again and again. Is there something written in the reduce method itself? Can you explain me please?
Note that array.reduce: reduce calls the callback, as a function, once for each element after the first element present in the array, in ascending order. You could understand the reduce as a array.map but the goal of it is to change the array to a singe output. It will loop over the whole array same with the forEach/map/... Check below example, even though you don't do anything, like return or anything else to array.reduce, it will still work and iterate the array You could check here for more But of course if you don't use return for array.reduce, there will be no benefit for you to use array.reduce const array = [7, 2, 4, 1, 10, 6, 5, 11] const max = array.reduce((acc, val) => { console.log(val) }, 0) console.log(max)
As per the docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce The reduce() method executes a user-supplied “reducer” callback function on each element of the array,
The reduce method's implementation resembles something like this: Array.prototype.reduce = function(callback, initialValue) { var acc = initialValue; for(var i = 0; i < this.length; i++) { acc = callback(acc, this[i], i); } return acc; } The reduce methods iteration condition is the array length. The same goes for map.
Use reduce and recursion in javascript to flatten nested array
I have found this interesting code example which flattens an array using the recursion and reduce function instead of flat. I get what it does except the following: acc = acc.concat(flatWithRec(item)); why accumulator is being reassign? how is it possible in reduce function? and why use concat here? return acc; why acc is being returned? is it a new Flat Array each time function is called? is there a way to, still, use recursion and make it easier for the reader to understand? Please clarify function flatWithRec(arr) { const flatArray = arr.reduce((acc, item) => { if (Array.isArray(item)) { acc = acc.concat(flatWithRec(item)); } else { acc.push(item); } return acc; }, []); return flatArray; } console.log(flatWithRec([1, [2, 3, [4], [5, 6, [7]] ]])) // output: [1, 2, 3, 4, 5, 6, 7])
The accumulator is an array. You reassign it to give it the new array containing the items of the one you have at the beginning of the loop and the items of the array items to add. As said in the comments, acc.concat returns a new array containing the items of the arrays passed in parameter. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat You need to return the accumulator at the end of each loop for the new value to be taken in account at the next loop. Javascript recursive array flattening
acc = acc.concat(flatWithRec(item)); why accumulator is being reassign? how is it possible in reduce function? and why use concat here? The accumulator (acc) is a function argument, and can be re-assigned, although it's not a good practice to do so. Concat combines two items (array or otherwise), and returns a new array, so you need to assign it to the acc as the result of the current loop. return acc; why acc is being returned? is it a new Flat Array each time function is called? The accumulator holds the current state of the reduced items, in your case the current flat array after each loop. You need to return it, so the next loop can continue to accumulate. is there a way to, still, use recursion and make it easier for the reader to understand? My take on flatWithRec - always concat, but if it's an array call flatWithRec on it before concatenating: function flatWithRec(arr) { return arr.reduce((acc, item) => acc.concat( Array.isArray(item) ? flatWithRec(item) : item ), []); } const result = flatWithRec([1, [2, 3, [4], [5, 6, [7]]]]) console.log(result) // output: [1, 2, 3, 4, 5, 6, 7])
So, the callback for the reduce method runs for every item in the array and whatever is returned from iteration x is passed as the first argument to iteration x+1. So, it is essential to make sure that during every iteration the correct state is returned. acc = acc.concat(flatWithRec(item)) Why accumulator is being reassigned? How is it possible in reduce function? And why use concat here? So, we are assigning acc the return value of concat because concat does not change the original array, it returns a fresh array. Accumulator is just like any other parameter so you can reassign. Not at all necessary to use concat (see my solution at the end using push and spread). return acc; Why acc is being returned? Is it a new flat array each time the function is called? As, already mentioned you need to return the correct state from the callback. And yes, during each iteration a new array is being created (which is not that performant, see my solution at the end) Is there a way to still use recursion and make it easier for the reader to understand? Easier for reader I can't tell, but here's my solution using => functions and spread. const flatWithRec = (arr) => arr.reduce((acc, item) => ( Array.isArray(item) ? acc.push(...flatWithRec(item)) : acc.push(item), acc ), []); console.log(flatWithRec([1, [2, 3, [4], [5, 6, [7]]]]));
Yes, there is a way to use recursion and make it easier to understand: function f(A, i=0){ return i == A.length ? [] : (Array.isArray(A[i]) ? f(A[i]) : [A[i]]).concat(f(A, i+1)); } var A = [1, [2, 3, [4], [5, 6, [7]]]]; console.log(JSON.stringify(A)); console.log(JSON.stringify(f(A)));
How to insert an item into an array at a specific index (JavaScript)
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"} ) );