Replace array entry with spread syntax in one line of code? [duplicate] - javascript

This question already has answers here:
Replace element at specific position in an array without mutating it
(9 answers)
Closed 3 months ago.
I'm replacing an item in a react state array by using the ... spread syntax. This works:
let newImages = [...this.state.images]
newImages[4] = updatedImage
this.setState({images:newImages})
Would it be possible to do this in one line of code? Something like this? (this doesn't work obviously...)
this.setState({images: [...this.state.images, [4]:updatedImage})

use Array.slice
this.setState({
images: [
...this.state.images.slice(0, 4),
updatedImage,
...this.state.images.slice(5),
],
});
Edit from original post: changed the 3 o a 4 in the second parameter of the slice method since the second parameter points to the member of the array that is beyond the last one kept, it now correctly answers the original question.

Once the change array by copy proposal is widely supported (it's at Stage 3, so should be finding its way into JavaScript engines), you'll be able to do this with the new with method:
// Using a Stage 3 proposal, not widely supported yet as of Nov 17 2022
this.setState({images: this.state.images.with(4, updatedImage)});
Until then, Object.assign does the job:
this.setState({images: Object.assign([], this.state.images, {4: updatedImage}));
...but involves a temporary object (the one at the end). Still, just the one temp object... If you do this with slice and spreading out arrays, it involve several more temporary objects (the two arrays from slice, the iterators for them, the result objects created by calling the iterator's next function [inside the ... handle], etc.).
It works because normal JS arrays aren't really arrays1 (this is subject to optimization, of course), they're objects with some special features. Their "indexes" are actually property names meeting certain criteria2. So there, we're spreading out this.state.images into a new array, passing that into Object.assign as the target, and giving Object.assign an object with a property named "4" (yes, it ends up being a string but we're allowed to write it as a number) with the value we want to update.
Live Example:
const a = [0, 1, 2, 3, 4, 5, 6, 7];
const b = Object.assign([], a, {4: "four"});
console.log(b);
If the 4 can be variable, that's fine, you can use a computed property name (new in ES2015):
let n = 4;
this.setState({images: Object.assign([], this.state.images, {[n]: updatedImage}));
Note the [] around n.
Live Example:
const a = [0, 1, 2, 3, 4, 5, 6, 7];
const index = 4;
const b = Object.assign([], a, {[index]: "four"});
console.log(b);
1 Disclosure: That's a post on my anemic little blog.
2 It's the second paragraph after the bullet list:
An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 253-1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 232-1.
So that Object.assign does the same thing as your create-the-array-then-update-index-4.

You can use map:
const newImages = this.state.images
.map((image, index) => index === 4 ? updatedImage : image)

You can convert the array to objects (the ...array1), replace the item (the [1]:"seven"), then convert it back to an array (Object.values) :
array1 = ["one", "two", "three"];
array2 = Object.values({...array1, [1]:"seven"});
console.log(array1);
console.log(array2);

Here is my self explaning non-one-liner
const wantedIndex = 4;
const oldArray = state.posts; // example
const updated = {
...oldArray[wantedIndex],
read: !oldArray[wantedIndex].read // attributes to change...
}
const before = oldArray.slice(0, wantedIndex);
const after = oldArray.slice(wantedIndex + 1);
const menu = [
...before,
updated,
...after
]

I refer to #Bardia Rastin solution, and I found that the solution has a mistake at the index value (it replaces item at index 3 but not 4).
If you want to replace the item which has index value, index, the answer should be
this.setState({images: [...this.state.images.slice(0, index), updatedImage, ...this.state.images.slice(index + 1)]})
this.state.images.slice(0, index) is a new array has items start from 0 to index - 1 (index is not included)
this.state.images.slice(index) is a new array has items starts from index and afterwards.
To correctly replace item at index 4, answer should be:
this.setState({images: [...this.state.images.slice(0, 4), updatedImage, ...this.state.images.slice(5)]})

first find the index, here I use the image document id docId as illustration:
const index = images.findIndex((prevPhoto)=>prevPhoto.docId === docId)
this.setState({images: [...this.state.images.slice(0,index), updatedImage, ...this.state.images.slice(index+1)]})

I have tried a lot of using the spread operator. I think when you use splice() it changes the main array. So the solution I discovered is to clone the array in new variables and then split it using the spread operator. The example I used.
var cart = [];
function addItem(item) {
let name = item.name;
let price = item.price;
let count = item.count;
let id = item.id;
cart.push({
id,
name,
price,
count,
});
return;
}
function removeItem(id) {
let itemExist = false;
let index = 0;
for (let j = 0; j < cart.length; j++) {
if (cart[j].id === id) { itemExist = true; break; }
index++;
}
if (itemExist) {
cart.splice(index, 1);
}
return;
}
function changeCount(id, newCount) {
let itemExist = false;
let index = 0;
for (let j = 0; j < cart.length; j++) {
console.log("J: ", j)
if (cart[j].id === id) {
itemExist = true;
index = j;
break;
}
}
console.log(index);
if (itemExist) {
let temp1 = [...cart];
let temp2 = [...cart];
let temp3 = [...cart];
cart = [...temp1.splice(0, index),
{
...temp2[index],
count: newCount
},
...temp3.splice(index + 1, cart.length)
];
}
return;
}
addItem({
id: 1,
name: "item 1",
price: 10,
count: 1
});
addItem({
id: 2,
name: "item 2",
price: 11,
count: 1
});
addItem({
id: 3,
name: "item 3",
price: 12,
count: 2
});
addItem({
id: 4,
name: "item 4",
price: 13,
count: 2
});
changeCount(4, 5);
console.log("AFTER CHANGE!");
console.log(cart);

Related

Compare two arrays of objects and if value for a certain key is missing, add an object with that value to the array - two ways

I have one array that has objects whose keys match another array of objects with the same keys, and the value for the first key is a Month index (0 = January, 1 = February, etc.) and the points may cross a year division (a:10, a:11, a:0, a:1)
However the first array may not have the same number of objects, and the values for the key on one object may not exist in the other object on the same key, and vice-versa.
Assuming both arrays are already ordered correctly, I want to compare those two arrays, and if one array is missing an object with a value for a key that the other one has, I want to add a new object to the first array with the same key and value, in the same position/index within the array that doesn't have it, as it is in the array that does have it.
let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]
arr1 is missing objects with an a value of 10, 11, and 2 that exist in arr2, and arr2 is missing objects with an a value of 3 and 4 that exist in arr1
What I want to end up with is:
arr1 = [{a:10, b:0},{a:11, b:3},{a:0, b:1},{a:1, b:3},{a:2, b:0},{a:3, b:18},{a:4, b:2}]
arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1},{a:3, b:0},{a:4, b:0}]
Now arr1 has new items/objects for a:10, a:11, and a:2 while
arr2 has new items for a:3 and a:4, all of which have a b value of 0;
I've tried plotting this out on paper to see logically what I would do physically, but I just can't get my head around it, so a "For Dummies" answer would be really helpful. I thought I had learned enough to be employable, but this is really sticking me up, and it's just for a simple HTML5 canvas line graph. I'm getting data from a DB to compare two lines on the same graph, where there may not be data for one month for one type of data, but there is for the other type of data. And for those spots that don't exist in one or the other, I want to drop the line down to 0 on the Y axis, then back up to the next value. https://github.com/rmgreenstreet/custom-forms/blob/master/public/javascripts/canvasRender.js
Having just month indexes is not OK for proper sorting.
Just add some information about year and it will be done easily.
Without sorting it may look like:
// loop elements of the arr2 with reduce,
// if there is any element in arr1 with the same value of key 'a',
// result is the same as on the previous step
// if there is no elements of that kind, add new object {'a': arr2['a'], 'b' : 0} into arr1
function newArray (arr1, arr2) {
return arr2.reduce((result, obj2) => {
if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
return result;
}
return [...result, {['a'] : obj2['a'], 'b':0}];
}, arr1)
}
// now you can assign the result of newArray() to new variables
const arr1_ = newArray(arr1, arr2)
const arr2_ = newArray(arr2, arr1)
OP's final working function (having changed the a value to be a mm/yyyy string:
function equalize(arr1, arr2) {
let newArr = arr2.reduce(function (result, obj2) {
if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
return result;
}
return [...result, {'a' : obj2['a'], 'b':0}];
}, arr1);
newArr.sort(function (a, b) {
console.log(`a: ${a}`)
a = a.x.split('/');
b = b.x.split('/')
return new Date(a[1], a[0], 1) - new Date(b[1], b[0], 1)
});
return newArr;
};
The main annoyance of this task is checking for presence of items with certain values of a. A naive straightfoward solution would require iterating over arr2 for evey item of arr1 and vice versa, which makes it O(n2), i.e. slow.
An alternative approach suggests employing objects as fast lookup maps. The idea is to turn your array inside out, use as as keys and bs as values.
let arr1 = [{a:1, b:1},{a:2, b:3},{a:4, b:18},{a:5, b:2}]
let arr2 = [{a:2, b:2},{a:3, b:4},{a:4, b:8},{a:6, b:5},{a:7, b:1}]
// Using objects as lookup maps.
let m1 = {}
let m2 = {}
// Filling the maps.
// The 'a' becomes a key, the 'b' becomes a value.
arr1.forEach(v => m1[v.a] = v.b)
arr2.forEach(v => m2[v.a] = v.b)
// Iterating over the keys of m1, checking if m2 has that key,
// if not - adding it with a value of 0.
for (let f in m1) m2[f] || (m2[f] = 0)
// The same goes for m2 -> m1.
for (let f in m2) m1[f] || (m1[f] = 0)
// At this point both m1 & m2 have all the keys from both arrays without gaps.
let res1 = []
let res2 = []
// Assembling the resulting arrays.
for (let f in m1) res1.push({a: f, b: m1[f]})
for (let f in m2) res2.push({a: f, b: m2[f]})
Pardon my inclination for one-liners.
Here is my solution. I am using lodash for helper functions.
function combineArrays (a, b) {
Object.keys(b).forEach(function (k) {
const key = parseInt(k);
if (!a[key]) {
a[key] = b[key];
a[key].b = 0;
}
});
return _.values(a);
}
Working code snippet
// const arr1 = [{ a: 1, b: 1 }, { a: 2, b: 3 }, { a: 4, b: 18 }, { a: 5, b: 2 }];
// const arr2 = [{ a: 2, b: 2 }, { a: 3, b: 4 }, { a: 4, b: 8 }, { a: 6, b: 5 }, { a: 7, b: 1 }];
let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]
const arr1Map = _.keyBy(arr1, 'a');
const arr2Map = _.keyBy(arr2, 'a');
function combineArrays(a1Map, a2Map) {
const a = _.cloneDeep(a1Map);
const b = _.cloneDeep(a2Map);
Object.keys(b).forEach(function(k) {
const key = parseInt(k);
if (!a[key]) {
a[key] = b[key];
a[key].b = 0;
}
});
return _.values(a);
}
console.log(combineArrays(arr1Map, arr2Map));
console.log(combineArrays(arr2Map, arr1Map));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
You should loop through the first array, and check if each key is in the second array. If it's not you should insert an item using arr.splice().
If you know both lists are sorted, ie the keys are in order, then you can also don't need to check the whole array for the new key.
let j = 0;
for (let i = 0; i < arr1.length; i++) {
let possiblyMissingKey = arr1[i].a;
while (arr2[j].a < possiblyMissingKey && j < arr2.length)
j++;
if (arr2[j].a != possiblyMissingKey) {
let itemToInsert = {a:possiblyMissingKey, b:0};
arr2.splice(j, 0, itemToInsert);
}
}
After you go through the first array, do the second array.

How to check in array how much times each word repeats itself and push it to another array [duplicate]

This question already has answers here:
Counting the occurrences / frequency of array elements
(39 answers)
Closed 5 years ago.
Lets say i have an array like this:
let votesArr = [yes,no,yes,no,yes];
and i want to to count how much times every word repeats itself and push to another so the output looks like this:
let votesData = [3,2]; // 3 for three yeses and 2 for two nos.
and i want to to work on many types of arrays like this, lets say an array that has 3 or 4 unique word.
I'm trying for a lot of time already and can't do that.
You could use the power of Map.
var array = ['yes', 'no', 'yes', 'no', 'yes'],
map = new Map,
result;
array.forEach(v => map.set(v, (map.get(v) || 0) + 1));
result = [...map.values()];
console.log(result);
Just returning a plain array of counts will not make sense I guess. It should be more like below. If you don't want this output then just map the values to form an array.
{
"yes": 3,
"no": 2
}
let votesArr = ["yes","no","yes","no","yes"];
const mappedArr = votesArr.reduce((a, b) => {
a[b] = a[b] || 0;
a[b] += 1;
return a;
}, {});
console.log(mappedArr);
You can do this as follows:
let votesArr = ["yes","no","yes","no","yes"];
let countSummary = votesArr.reduce( (count, val) => {
if(!count[val]) { count[val] = 0 }
count[val]++;
return count;
}, {})
console.log(countSummary)// {yes: 3, no: 2}
let countSummmaryArr = Object.keys(countSummary).map(k=>countSummary[k]);
console.log(countSummmaryArr )// [3,2]
The way this works is that the .reduce counts every instance to a map of values, and the .map converts it to an array of the values.
The below does what you need, although I'm sure it could be cleaned up a bit.
var data = ["Unsure", "Yes", "Yes", "No", "Yes", "No", "Maybe", "Unsure"];
var counts = {};
for (var i = 0; i < data.length; i++) {
(counts[data[i]]) ? counts[data[i]]++ : counts[data[i]] = 1;
}
// counts = {Unsure: 2, Yes: 3, No: 2, Maybe: 1}
You can do like this
let votesArr = ['yes', 'no', 'yes', 'no', 'yes'];
// Create an empty object to store array item as key & its
// number of repeat as value
var itemObj = {};
// loop over it and store the value in the object
var m = votesArr.forEach(function(item) {
if (!itemObj[item]) {
itemObj[item] = 1
} else {
itemObj[item] = itemObj[item] + 1
}
});
// Use object.values to retrive the value
console.log(Object.values(itemObj))

JavaScript move an item of an array to the front

I want to check if an array contains "role". If it does, I want to move the "role" to the front of the array.
var data= ["email","role","type","name"];
if ("role" in data) data.remove(data.indexOf("role")); data.unshift("role")
data;
Here, I got the result:
["role", "email", "role", "type", "name"]
How can I fix this?
You can sort the array and specify that the value "role" comes before all other values, and that all other values are equal:
var first = "role";
data.sort(function(x,y){ return x == first ? -1 : y == first ? 1 : 0; });
Demo: http://jsfiddle.net/Guffa/7ST24/
The cleanest solution in ES6 in my opinion:
let data = ["email","role","type","name"];
data = data.filter(item => item !== "role");
data.unshift("role");
let data = [0, 1, 2, 3, 4, 5];
let index = 3;
data.unshift(data.splice(index, 1)[0]);
// data = [3, 0, 1, 2, 4, 5]
My first thought would be:
var data= ["email","role","type","name"];
// if it's not there, or is already the first element (of index 0)
// then there's no point going further:
if (data.indexOf('role') > 0) {
// find the current index of 'role':
var index = data.indexOf('role');
// using splice to remove elements from the array, starting at
// the identified index, and affecting 1 element(s):
data.splice(index,1);
// putting the 'role' string back in the array:
data.unshift('role');
}
console.log(data);
To revise, and tidy up a little:
if (data.indexOf('role') > 0) {
data.splice(data.indexOf('role'), 1);
data.unshift('role');
}
References:
Array.indexOf().
Array.prototype.splice().
Array.unshift().
Here is an immutable solution if needed :
const newData = [
data.find(item => item === 'role'),
...data.filter(item => item !== 'role'),
],
If you don't want to alter the existing array, you can use ES6 destructuring with the filter method to create a new copy while maintaining the order of the other items.
const data = ["email", "role", "type", "name"];
const newData = ['role', ...data.filter(item => item !== 'role')];
If you have an array of objects you could shift the start-index with splice and push. Splice replaces the original array with the part of the array starting from the desired index and returns the part it removes (the stuff before the index) which you push.
let friends = [{
id: 1,
name: "Sam",
},
{
id: 2,
name: "Steven",
},
{
id: 3,
name: "Tom",
},
{
id: 4,
name: "Nora",
},
{
id: 5,
name: "Jessy",
}
];
const tomsIndex = friends.findIndex(friend => friend.name == 'Tom');
friends.push(...friends.splice(0, tomsIndex));
console.log(friends);
To check whether an item exists in an array you should to use .includes() instead of in (as already noted here, in is for properties in objects).
This function does what you are looking for:
(removes the item from the position it is in and reads
in front)
data = ["email","role","type","name"];
moveToFirst("role", data);
function moveToFirst( stringToMove, arrayIn ){
if ( arrayIn.includes(stringToMove) ){
let currentIndex = arrayIn.indexOf(stringToMove);
arrayIn.splice(currentIndex, 1);
arrayIn.unshift(stringToMove);
}
}
console.log(data);
Similar to #Tandroid's answer but a more general solution:
const putItemsFirst = ({ findFunction, array }) => [
...array.filter(findFunction),
...array.filter(item => !findFunction(item)),
];
Can be used like this
putItemsFirst({
array: ["email","role","type","name"],
findFunction: item => item === 'role',
})
Something similar to this is what I ended up using,
I would go with this ES6 solution. It doesn't mutate the original array(considering it's not nested), doesn't traverse through the array(filter) and you're not just limited to 0th index for shifting the array item.
const moveArrayItem = (array, fromIndex, toIndex) => {
const arr = [...array];
arr.splice(toIndex, 0, ...arr.splice(fromIndex, 1));
return arr;
}
const arr = ["a", "b", "c", "d", "e", "f", "g"];
console.log(moveArrayItem(arr, 4, 0))
// [ 'e', 'a', 'b', 'c', 'd', 'f', 'g' ]
The most readable way in my opinion.
array.sort((a, b) => (a === value && -1) || (b === value && 1) || 0)
var data= ["email","role","type","name"];
data.splice(data.indexOf("role"), 1);
data.unshift('role');
You could take the delta of the check with the wanted value at top.
var data = ["email", "role", "type", "name"];
data.sort((a, b) => (b === 'role') - (a === 'role'));
console.log(data);
A reusable ES6/Typescript solution:
const moveToStart = <T>(array: T[], predicate: (item: T) => boolean): T[] => {
return array.sort((a, b) => {
if (predicate(a)) return -1;
if (predicate(b)) return 1;
return 0;
});
};
const data = ["email", "role", "type", "name"];
const result = moveToStart(data, (item) => item === "role"))
the in operator is about properties, not about items in arrays. See How do I check if an array includes an object in JavaScript? for what to use else.
You're missing braces around the two (!) statements in your if-block
I'm not sure whether that .remove() function you're using does take an index of an item.
Using lodash _.sortBy. If the item is role, it will be sorted first, otherwise second. This works fine too if there is no role
var data = ["email", "role", "type", "name"];
var sorted = _.sortBy(data, function(item) {
return item === 'role' ? 0 : 1;
});
console.log(sorted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Just wanted to drop this on here since according to other comments Guffa's answer seems to be gaining traction, the final tertiary - which was one of the negative comments on that answer is unnecessary. Also using arrow functions makes it seem much cleaner.
Also, it is easily expandable to handling Arrays of objects.
const first = "role";
data.sort((x, y) => first === x ? -1 : first === y)
I believe this should also handle the worry of the rest of the array being affected. When the sort function returns a number less than 0 (first === x), the element will move toward the start of the Array, when it returns 0 (first !== y), there will be no movement, and when a number greater than 0 (first === y), x will move toward the end of the Array, all in relation to x and y. Therefore, when neither x or y are equivalent to the desired first element (or it's identifier in the case of sorting objects), there will be no movement of the two in relation to each other.
For an object:
const unsorted = [{'id': 'test'}, {'id': 'something'}, {'id': 'else'}];
const first = 'something';
const sorted = unsorted.sort((x,y) => x['id'] === first ? -1 : y['id'] === first);
My solution is a bit different as it mutates original array instead of creating a new one.
It will move given item to start of the array and move item that was previously at start in the place of requested item.
function moveElementToStart<T>(items: T[], item: T) {
const itemIndex = items.indexOf(item);
// Item is not found or it is already on start
if (itemIndex === -1 || itemIndex === 0) return;
// Get item that is currently at start
const currentItemAtStart = items[0];
// Swap this item position with item we want to put on start
items[0] = item;
items[itemIndex] = currentItemAtStart;
}
Generalized one-liners:
const data = ["a", "b", "c", "d", "e", "f"];
const [from, take] = [3, 2];
data.unshift(...data.splice(from, take));
// alternatively
data = [...data.splice(from, take), ...data];
// ["d", "e", "a", "b", "c", "f"]
const moveToFront = (arr, queryStr) =>
arr.reduce((acc, curr) => {
if (queryStr === curr) {
return [curr, ...acc];
}
return [...acc, curr];
}, []);
const data = ['email', 'role', 'type', 'name'];
console.log(moveToFront(data, 'role'))
const moveTargetToBeginningOfArray = (arr, target) => {
// loop through array
for (let i = 0; i < arr.length; i++){
// if current indexed element is the target
if(arr[i] === target){
// remove that target element
arr.splice(i, 1)
// then add a target element to the beginning of the array
arr.unshift(target)
}
}
return arr;
};
// quick sanity check, before and after both are correct
const arrayOfStrings = ["email", "role", "type", "name", "role", "role"];
console.log('before:', arrayOfStrings)
console.log('after:', moveTargetToBeginningOfArray(arrayOfStrings, "role"))
// this would also work for numbers
var arrayOfNumbers = [2,4,0,3,0,1,0]
console.log('before:', arrayOfNumbers)
console.log('after:', moveTargetToBeginningOfArray(arrayOfNumbers, 0))
function unshiftFrom(arr, index) {
if (index > -1 && index < arr.length) { // validate index
var [itemToMove] = arr.splice(index, 1)
arr.unshift(itemToMove)
}
return arr // optional
}
//we can do this from scratch
let tempList=["person1","person2","person3"];
let result=[];
//suppose i need to move "person2" to first place
let movableValue=null;
let query="person2"; //here you could use any type of query based on your problem
tempList.map((e)=>{
if(e!==query){
result.push(e);
}else if(e===query){
movableValue=e;
}
})
if(movableValue!==null){
result.unshift(movableValue);
}
console.log(result)
)
var i = -1;
while (i < data.length) {
if (data[i] === "role") {
data.splice(i, 1);
break;
}
i++;
}
data.unshift("role");
indexOf only has limited browser support, not being recognized by IE7-8. So I wouldn't use it if I were you, even at the expense of a few lines' worth of code conciseness. You also want to put a semicolon at the end of the "unshift" statement. splice()'s first argument specifies the index to start removing elements, and the second argument specifies the number of arguments to remove.
data.unshift(data.splice(data.indexOf('role'), 1)[0])
data.indexOf('role') will find the index of 'role' in the array and then the original array is spliced to remove the 'role' element, which is added to the beginning of the array using unshift
var data= ["email","role","type","name"];
if ("role" in data) data.splice(data.indexOf("role"),1); data.unshift("role");
data;

How to replace item in array?

Each item of this array is some number:
var items = Array(523,3452,334,31, ...5346);
How to replace some item with a new one?
For example, we want to replace 3452 with 1010, how would we do this?
var index = items.indexOf(3452);
if (index !== -1) {
items[index] = 1010;
}
Also it is recommend you not use the constructor method to initialize your arrays. Instead, use the literal syntax:
var items = [523, 3452, 334, 31, 5346];
You can also use the ~ operator if you are into terse JavaScript and want to shorten the -1 comparison:
var index = items.indexOf(3452);
if (~index) {
items[index] = 1010;
}
Sometimes I even like to write a contains function to abstract this check and make it easier to understand what's going on. What's awesome is this works on arrays and strings both:
var contains = function (haystack, needle) {
return !!~haystack.indexOf(needle);
};
// can be used like so now:
if (contains(items, 3452)) {
// do something else...
}
Starting with ES6/ES2015 for strings, and proposed for ES2016 for arrays, you can more easily determine if a source contains another value:
if (haystack.includes(needle)) {
// do your thing
}
The Array.indexOf() method will replace the first instance. To get every instance use Array.map():
a = a.map(function(item) { return item == 3452 ? 1010 : item; });
Of course, that creates a new array. If you want to do it in place, use Array.forEach():
a.forEach(function(item, i) { if (item == 3452) a[i] = 1010; });
Answer from #gilly3 is great.
Replace object in an array, keeping the array order unchanged
I prefer the following way to update the new updated record into my array of records when I get data from the server. It keeps the order intact and quite straight forward one liner.
users = users.map(u => u.id !== editedUser.id ? u : editedUser);
var users = [
{id: 1, firstname: 'John', lastname: 'Ken'},
{id: 2, firstname: 'Robin', lastname: 'Hood'},
{id: 3, firstname: 'William', lastname: 'Cook'}
];
var editedUser = {id: 2, firstname: 'Michael', lastname: 'Angelo'};
users = users.map(u => u.id !== editedUser.id ? u : editedUser);
console.log('users -> ', users);
My suggested solution would be:
items.splice(1, 1, 1010);
The splice operation will start at index 1, remove 1 item in the array (i.e. 3452), and will replace it with the new item 1010.
Use indexOf to find an element.
var i = items.indexOf(3452);
items[i] = 1010;
First method
Best way in just one line to replace or update item of array
array.splice(array.indexOf(valueToReplace), 1, newValue)
Eg:
let items = ['JS', 'PHP', 'RUBY'];
let replacedItem = items.splice(items.indexOf('RUBY'), 1, 'PYTHON')
console.log(replacedItem) //['RUBY']
console.log(items) //['JS', 'PHP', 'PYTHON']
Second method
An other simple way to do the same operation is :
items[items.indexOf(oldValue)] = newValue
Easily accomplished with a for loop.
for (var i = 0; i < items.length; i++)
if (items[i] == 3452)
items[i] = 1010;
If using a complex object (or even a simple one) and you can use es6, Array.prototype.findIndex is a good one. For the OP's array, they could do,
const index = items.findIndex(x => x === 3452)
items[index] = 1010
For more complex objects, this really shines. For example,
const index =
items.findIndex(
x => x.jerseyNumber === 9 && x.school === 'Ohio State'
)
items[index].lastName = 'Utah'
items[index].firstName = 'Johnny'
You can edit any number of the list using indexes
for example :
items[0] = 5;
items[5] = 100;
ES6 way:
const items = Array(523, 3452, 334, 31, ...5346);
We wanna replace 3452 with 1010, solution:
const newItems = items.map(item => item === 3452 ? 1010 : item);
Surely, the question is for many years ago and for now I just prefer to use immutable solution, definitely, it is awesome for ReactJS.
For frequent usage I offer below function:
const itemReplacer = (array, oldItem, newItem) =>
array.map(item => item === oldItem ? newItem : item);
A functional approach to replacing an element of an array in javascript:
const replace = (array, index, ...items) => [...array.slice(0, index), ...items, ...array.slice(index + 1)];
The immutable way to replace the element in the list using ES6 spread operators and .slice method.
const arr = ['fir', 'next', 'third'], item = 'next'
const nextArr = [
...arr.slice(0, arr.indexOf(item)),
'second',
...arr.slice(arr.indexOf(item) + 1)
]
Verify that works
console.log(arr) // [ 'fir', 'next', 'third' ]
console.log(nextArr) // ['fir', 'second', 'third']
Replacement can be done in one line:
var items = Array(523, 3452, 334, 31, 5346);
items[items.map((e, i) => [i, e]).filter(e => e[1] == 3452)[0][0]] = 1010
console.log(items);
Or create a function to reuse:
Array.prototype.replace = function(t, v) {
if (this.indexOf(t)!= -1)
this[this.map((e, i) => [i, e]).filter(e => e[1] == t)[0][0]] = v;
};
//Check
var items = Array(523, 3452, 334, 31, 5346);
items.replace(3452, 1010);
console.log(items);
var items = Array(523,3452,334,31,5346);
If you know the value then use,
items[items.indexOf(334)] = 1010;
If you want to know that value is present or not, then use,
var point = items.indexOf(334);
if (point !== -1) {
items[point] = 1010;
}
If you know the place (position) then directly use,
items[--position] = 1010;
If you want replace few elements, and you know only starting position only means,
items.splice(2, 1, 1010, 1220);
for more about .splice
The easiest way is to use some libraries like underscorejs and map method.
var items = Array(523,3452,334,31,...5346);
_.map(items, function(num) {
return (num == 3452) ? 1010 : num;
});
=> [523, 1010, 334, 31, ...5346]
If you want a simple sugar sintax oneliner you can just:
(elements = elements.filter(element => element.id !== updatedElement.id)).push(updatedElement);
Like:
let elements = [ { id: 1, name: 'element one' }, { id: 2, name: 'element two'} ];
const updatedElement = { id: 1, name: 'updated element one' };
If you don't have id you could stringify the element like:
(elements = elements.filter(element => JSON.stringify(element) !== JSON.stringify(updatedElement))).push(updatedElement);
var index = Array.indexOf(Array value);
if (index > -1) {
Array.splice(index, 1);
}
from here you can delete a particular value from array and based on the same index
you can insert value in array .
Array.splice(index, 0, Array value);
Well if anyone is interresting on how to replace an object from its index in an array, here's a solution.
Find the index of the object by its id:
const index = items.map(item => item.id).indexOf(objectId)
Replace the object using Object.assign() method:
Object.assign(items[index], newValue)
items[items.indexOf(3452)] = 1010
great for simple swaps. try the snippet below
const items = Array(523, 3452, 334, 31, 5346);
console.log(items)
items[items.indexOf(3452)] = 1010
console.log(items)
Here is the basic answer made into a reusable function:
function arrayFindReplace(array, findValue, replaceValue){
while(array.indexOf(findValue) !== -1){
let index = array.indexOf(findValue);
array[index] = replaceValue;
}
}
Here's a one liner. It assumes the item will be in the array.
var items = [523, 3452, 334, 31, 5346]
var replace = (arr, oldVal, newVal) => (arr[arr.indexOf(oldVal)] = newVal, arr)
console.log(replace(items, 3452, 1010))
const items = Array(1, 2, 3, 4, 5);
console.log(items)
items[items.indexOf(2)] = 1010
console.log(items)
First, rewrite your array like this:
var items = [523,3452,334,31,...5346];
Next, access the element in the array through its index number. The formula to determine the index number is: n-1
To replace the first item (n=1) in the array, write:
items[0] = Enter Your New Number;
In your example, the number 3452 is in the second position (n=2). So the formula to determine the index number is 2-1 = 1. So write the following code to replace 3452 with 1010:
items[1] = 1010;
I solved this problem using for loops and iterating through the original array and adding the positions of the matching arreas to another array and then looping through that array and changing it in the original array then return it, I used and arrow function but a regular function would work too.
var replace = (arr, replaceThis, WithThis) => {
if (!Array.isArray(arr)) throw new RangeError("Error");
var itemSpots = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] == replaceThis) itemSpots.push(i);
}
for (var i = 0; i < itemSpots.length; i++) {
arr[itemSpots[i]] = WithThis;
}
return arr;
};
presentPrompt(id,productqty) {
let alert = this.forgotCtrl.create({
title: 'Test',
inputs: [
{
name: 'pickqty',
placeholder: 'pick quantity'
},
{
name: 'state',
value: 'verified',
disabled:true,
placeholder: 'state',
}
],
buttons: [
{
text: 'Ok',
role: 'cancel',
handler: data => {
console.log('dataaaaname',data.pickqty);
console.log('dataaaapwd',data.state);
for (var i = 0; i < this.cottonLists.length; i++){
if (this.cottonLists[i].id == id){
this.cottonLists[i].real_stock = data.pickqty;
}
}
for (var i = 0; i < this.cottonLists.length; i++){
if (this.cottonLists[i].id == id){
this.cottonLists[i].state = 'verified';
}
}
//Log object to console again.
console.log("After update: ", this.cottonLists)
console.log('Ok clicked');
}
},
]
});
alert.present();
}
As per your requirement you can change fields and array names.
thats all. Enjoy your coding.
The easiest way is this.
var items = Array(523,3452,334,31, 5346);
var replaceWhat = 3452, replaceWith = 1010;
if ( ( i = items.indexOf(replaceWhat) ) >=0 ) items.splice(i, 1, replaceWith);
console.log(items);
>>> (5) [523, 1010, 334, 31, 5346]
When your array have many old item to replace new item, you can use this way:
function replaceArray(array, oldItem, newItem) {
for (let i = 0; i < array.length; i++) {
const index = array.indexOf(oldItem);
if (~index) {
array[index] = newItem;
}
}
return array
}
console.log(replaceArray([1, 2, 3, 2, 2, 8, 1, 9], 2, 5));
console.log(replaceArray([1, 2, 3, 2, 2, 8, 1, 9], 2, "Hi"));
let items = Array(523,3452,334,31, 5346);
items[0]=1010;
This will do the job
Array.prototype.replace = function(a, b) {
return this.map(item => item == a ? b : item)
}
Usage:
let items = ['hi', 'hi', 'hello', 'hi', 'hello', 'hello', 'hi']
console.log(items.replace('hello', 'hi'))
Output:
['hi', 'hi', 'hi', 'hi', 'hi', 'hi', 'hi']
The nice thing is, that EVERY array will have .replace() property.

Fastest way to duplicate an array in JavaScript - slice vs. 'for' loop

In order to duplicate an array in JavaScript: Which of the following is faster to use?
Slice method
var dup_array = original_array.slice();
For loop
for(var i = 0, len = original_array.length; i < len; ++i)
dup_array[i] = original_array[i];
I know both ways do only a shallow copy: if original_array contains references to objects, objects won't be cloned, but only the references will be copied, and therefore both arrays will have references to the same objects.
But this is not the point of this question.
I'm asking only about speed.
There are at least 6 (!) ways to clone an array:
loop
slice
Array.from()
concat
spread syntax (FASTEST)
map A.map(function(e){return e;});
There has been a huuuge BENCHMARKS thread, providing following information:
for blink browsers slice() is the fastest method, concat() is a bit slower, and while loop is 2.4x slower.
for other browsers while loop is the fastest method, since those browsers don't have internal optimizations for slice and concat.
This remains true in Jul 2016.
Below are simple scripts that you can copy-paste into your browser's console and run several times to see the picture. They output milliseconds, lower is better.
while loop
n = 1000*1000;
start = + new Date();
a = Array(n);
b = Array(n);
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);
slice
n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);
Please note that these methods will clone the Array object itself, array contents however are copied by reference and are not deep cloned.
origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
Technically slice is the fastest way. However, it is even faster if you add the 0 begin index.
myArray.slice(0);
is faster than
myArray.slice();
https://jsben.ch/F0SZ3
what about es6 way?
arr2 = [...arr1];
Easiest way to deep clone Array or Object:
var dup_array = JSON.parse(JSON.stringify(original_array))
🏁 Fastest Way to Clone an Array
I made this very plain utility function to test the time that it takes to clone an array. It is not 100% reliable however it can give you a bulk idea as for how long it takes to clone an existing array:
function clone(fn) {
const arr = [...Array(1000000)];
console.time('timer');
fn(arr);
console.timeEnd('timer');
}
And tested different approach:
1) 5.79ms -> clone(arr => Object.values(arr));
2) 7.23ms -> clone(arr => [].concat(arr));
3) 9.13ms -> clone(arr => arr.slice());
4) 24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.push(val); } return a; });
5) 30.02ms -> clone(arr => [...arr]);
6) 39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7) 99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));
UPDATE:
Tests were made back in 2018, so today most likely you'll get different result with current browsers.
Out of all of those, the only way to deep clone an array is by using JSON.parse(JSON.stringify(arr)).
That said, do not use the above if your array might include functions as it will return null.Thank you #GilEpshtain for this update.
var cloned_array = [].concat(target_array);
I put together a quick demo: http://jsbin.com/agugo3/edit
My results on Internet Explorer 8 are 156, 782, and 750, which would indicate slice is much faster in this case.
a.map(e => e) is another alternative for this job. As of today .map() is very fast (almost as fast as .slice(0)) in Firefox, but not in Chrome.
On the other hand, if an array is multi-dimensional, since arrays are objects and objects are reference types, none of the slice or concat methods will be a cure... So one proper way of cloning an array is an invention of Array.prototype.clone() as follows.
Array.prototype.clone = function(){
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));
Fastest way to clone an Array of Objects will be using spread operator
var clonedArray=[...originalArray]
or
var clonedArray = originalArray.slice(0); //with 0 index it's little bit faster than normal slice()
but the objects inside that cloned array will still pointing at the old memory location. hence change to clonedArray objects will also change the orignalArray. So
var clonedArray = originalArray.map(({...ele}) => {return ele})
this will not only create new array but also the objects will be cloned to.
disclaimer if you are working with nested object in that case spread operator will work as SHALLOW CLONE. At that point better to use
var clonedArray=JSON.parse(JSON.stringify(originalArray));
Take a look at: link. It's not about speed, but comfort. Besides as you can see you can only use slice(0) on primitive types.
To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.
Example:
To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.
var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();
To copy or clone an object :
function cloneObject(source) {
for (i in source) {
if (typeof source[i] == 'source') {
this[i] = new cloneObject(source[i]);
}
else{
this[i] = source[i];
}
}
}
var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2= new cloneObject(obj1);
Source: link
ECMAScript 2015 way with the Spread operator:
Basic examples:
var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]
Try in the browser console:
var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)
var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);
References
6 Great Uses of the Spread Operator
Spread syntax
As #Dan said "This answer becomes outdated fast. Use benchmarks to check the actual situation", there is one specific answer from jsperf that has not had an answer for itself: while:
var i = a.length;
while(i--) { b[i] = a[i]; }
had 960,589 ops/sec with the runnerup a.concat() at 578,129 ops/sec, which is 60%.
This is the lastest Firefox (40) 64 bit.
#aleclarson created a new, more reliable benchmark.
Benchmark time!
function log(data) {
document.getElementById("log").textContent += data + "\n";
}
benchmark = (() => {
time_function = function(ms, f, num) {
var z = 0;
var t = new Date().getTime();
for (z = 0;
((new Date().getTime() - t) < ms); z++)
f(num);
return (z)
}
function clone1(arr) {
return arr.slice(0);
}
function clone2(arr) {
return [...arr]
}
function clone3(arr) {
return [].concat(arr);
}
Array.prototype.clone = function() {
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
function clone4(arr) {
return arr.clone();
}
function benchmark() {
function compare(a, b) {
if (a[1] > b[1]) {
return -1;
}
if (a[1] < b[1]) {
return 1;
}
return 0;
}
funcs = [clone1, clone2, clone3, clone4];
results = [];
funcs.forEach((ff) => {
console.log("Benchmarking: " + ff.name);
var s = time_function(2500, ff, Array(1024));
results.push([ff, s]);
console.log("Score: " + s);
})
return results.sort(compare);
}
return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();
console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>
The benchmark will run for 10s since you click the button.
My results:
Chrome (V8 engine):
1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)
Firefox (SpiderMonkey Engine):
1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)
Winner code:
function clone1(arr) {
return arr.slice(0);
}
Winner engine:
SpiderMonkey (Mozilla/Firefox)
It depends on the browser. If you look in the blog post Array.prototype.slice vs manual array creation, there is a rough guide to performance of each:
Results:
There is a much cleaner solution:
var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);
The length check is required, because the Array constructor behaves differently when it is called with exactly one argument.
Remember .slice() won't work for two-dimensional arrays. You'll need a function like this:
function copy(array) {
return array.map(function(arr) {
return arr.slice();
});
}
It depends on the length of the array. If the array length is <= 1,000,000, the slice and concat methods are taking approximately the same time. But when you give a wider range, the concat method wins.
For example, try this code:
var original_array = [];
for(var i = 0; i < 10000000; i ++) {
original_array.push( Math.floor(Math.random() * 1000000 + 1));
}
function a1() {
var dup = [];
var start = Date.now();
dup = original_array.slice();
var end = Date.now();
console.log('slice method takes ' + (end - start) + ' ms');
}
function a2() {
var dup = [];
var start = Date.now();
dup = original_array.concat([]);
var end = Date.now();
console.log('concat method takes ' + (end - start) + ' ms');
}
function a3() {
var dup = [];
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup.push(original_array[i]);
}
var end = Date.now();
console.log('for loop with push method takes ' + (end - start) + ' ms');
}
function a4() {
var dup = [];
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup[i] = original_array[i];
}
var end = Date.now();
console.log('for loop with = method takes ' + (end - start) + ' ms');
}
function a5() {
var dup = new Array(original_array.length)
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup.push(original_array[i]);
}
var end = Date.now();
console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}
a1();
a2();
a3();
a4();
a5();
If you set the length of original_array to 1,000,000, the slice method and concat method are taking approximately the same time (3-4 ms, depending on the random numbers).
If you set the length of original_array to 10,000,000, then the slice method takes over 60 ms and the concat method takes over 20 ms.
In ES6, you can simply utilize the Spread syntax.
Example:
let arr = ['a', 'b', 'c'];
let arr2 = [...arr];
Please note that the spread operator generates a completely new array, so modifying one won't affect the other.
Example:
arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']
A simple solution:
original = [1,2,3]
cloned = original.map(x=>x)
const arr = ['1', '2', '3'];
// Old way
const cloneArr = arr.slice();
// ES6 way
const cloneArrES6 = [...arr];
// But problem with 3rd approach is that if you are using muti-dimensional
// array, then only first level is copied
const nums = [
[1, 2],
[10],
];
const cloneNums = [...nums];
// Let's change the first item in the first nested item in our cloned array.
cloneNums[0][0] = '8';
console.log(cloneNums);
// [ [ '8', 2 ], [ 10 ], [ 300 ] ]
// NOOooo, the original is also affected
console.log(nums);
// [ [ '8', 2 ], [ 10 ], [ 300 ] ]
So, in order to avoid these scenarios to happen, use
const arr = ['1', '2', '3'];
const cloneArr = Array.from(arr);
There were several ways to clone an array. Basically, Cloning was categorized in two ways:
Shallow copy
Deep copy
Shallow copies only cover the 1st level of the array and the rest are
referenced. If you want a true copy of nested elements in the arrays, you’ll need a
deep clone.
Example :
const arr1 = [1,2,3,4,5,6,7]
// Normal Array (shallow copy is enough)
const arr2 = [1,2,3,[4],[[5]],6,7]
// Nested Array (Deep copy required)
Approach 1 : Using (...)Spread Operator (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]
Approach 2 : Using Array builtIn Slice method (Deep copy)
const newArray = arr1.slice() // [1,2,3,4,5,6,7]
Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1) // [1,2,3,4,5,6,7]
Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));) // [1,2,3,[4],[[5]],6,7]
Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)
Fast ways to duplicate an array in JavaScript in Order:
#1: array1copy = [...array1];
#2: array1copy = array1.slice(0);
#3: array1copy = array1.slice();
If your array objects contain some JSON-non-serializable content (functions, Number.POSITIVE_INFINITY, etc.) better to use
array1copy = JSON.parse(JSON.stringify(array1))
You can follow this code. Immutable way array clone. This is the perfect way to array cloning
const array = [1, 2, 3, 4]
const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)
If you want a REAL cloned object/array in JS with cloned references of all attributes and sub-objects:
export function clone(arr) {
return JSON.parse(JSON.stringify(arr))
}
ALL other operations do not create clones, because they just change the base address of the root element, not of the included objects.
Except you traverse recursive through the object-tree.
For a simple copy, these are OK. For storage address relevant operations I suggest (and in most all other cases, because this is fast!) to type convert into string and back in a complete new object.
If you are taking about slice it is used to copy elements from an array and create a clone with same no. of elements or less no. of elements.
var arr = [1, 2, 3 , 4, 5];
function slc() {
var sliced = arr.slice(0, 5);
// arr.slice(position to start copying master array , no. of items in new array)
console.log(sliced);
}
slc(arr);

Categories