How to replace elements in array with elements of another array - javascript

I want to replace elements in some array from 0 element, with elements of another array with variable length. Like:
var arr = new Array(10), anotherArr = [1, 2, 3], result;
result = anotherArr.concat(arr);
result.splice(10, anotherArr.length);
Is there some better way?

You can use the splice method to replace part of an array with items from another array, but you have to call it in a special way as it expects the items as parameters, not the array.
The splice method expects parameters like (0, anotherArr.Length, 1, 2, 3), so you need to create an array with the parameters and use the apply method to call the splice method with the parameters:
Array.prototype.splice.apply(arr, [0, anotherArr.length].concat(anotherArr));
Example:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
var anotherArr = [ 1, 2, 3 ];
Array.prototype.splice.apply(arr, [0, anotherArr.length].concat(anotherArr));
console.log(arr);
Output:
[ 1, 2, 3, 'd', 'e', 'f', 'g', 'h', 'i', 'j']
Demo: http://jsfiddle.net/Guffa/bB7Ey/

In ES6 with a single operation, you can do this to replace the first b.length elements of a with elements of b:
let a = [1, 2, 3, 4, 5]
let b = [10, 20, 30]
a.splice(0, b.length, ...b)
console.log(a) // -> [10, 20, 30, 4, 5]
It could be also useful to replace the entire content of an array, using a.length (or Infinity) in the splice length:
let a = [1, 2, 3, 4, 5]
let b = [10, 20, 30]
a.splice(0, a.length, ...b)
// or
// a.splice(0, Infinity, ...b)
console.log(a) // -> [10, 20, 30], which is the content of b
The a array's content will be entirely replaced by b content.
Note 1: in my opinion the array mutation should only be used in performance-critical applications, such as high FPS animations, to avoid creating new arrays. Normally I would create a new array maintaining immutability.
Note 2: if b is a very large array, this method is discouraged, because ...b is being spread in the arguments of splice, and there's a limit on the number of parameters a JS function can accept. In that case I encourage to use another method (or create a new array, if possible!).

In ES6, TypeScript, Babel or similar you can just do:
arr1.length = 0; // Clear your array
arr1.push(...arr2); // Push the second array using the spread opperator
Simple.

For anyone looking for a way to replace the entire contents of one array with entire contents of another array while preserving the original array:
Array.prototype.replaceContents = function (array2) {
//make a clone of the 2nd array to avoid any referential weirdness
var newContent = array2.slice(0);
//empty the array
this.length = 0;
//push in the 2nd array
this.push.apply(this, newContent);
};
The prototype function takes an array as a parameter which will serve as the new array content, clones it to avoid any weird referential stuff, empties the original array, and then pushes in the passed in array as the content. This preserves the original array and any references.
Now you can simply do this:
var arr1 = [1, 2, 3];
var arr2 = [3, 4, 5];
arr1.replaceContents(arr2);
I know this is not strictly what the initial question was asking, but this question comes up first when you search in google, and I figured someone else may find this helpful as it was the answer I needed.

You can just use splice, can add new elements while removing old ones:
var arr = new Array(10), anotherArr = [1, 2, 3];
arr.splice.apply(arr, [0, anotherArr.length].concat(anotherArr))
If you don't want to modify the arr array, you can use slice that returns a shallow copy of the array:
var arr = new Array(10), anotherArr = [1, 2, 3], result = arr.slice(0);
result.splice.apply(result, [0, anotherArr.length].concat(anotherArr));
Alternatively, you can use slice to cut off the first elements and adding the anotherArr on top:
result = anotherArr.concat(arr.slice(anotherArr.length));

I'm not sure if it's a "better" way, but at least it allows you to choose the starting index (whereas your solution only works starting at index 0). Here's a fiddle.
// Clone the original array
var result = arr.slice(0);
// If original array is no longer needed, you can do with:
// var result = arr;
// Remove (anotherArr.length) elements starting from index 0
// and insert the elements from anotherArr into it
Array.prototype.splice.apply(result, [0, anotherArr.length].concat(anotherArr));
(Damnit, so many ninjas. :-P)

You can just set the length of the array in this case. For more complex cases see #Guffa's answer.
var a = [1,2,3];
a.length = 10;
a; // [1, 2, 3, undefined x 7]

Related

Trying to create copies of an array using spread operator, but some how the array is being mutated

I'm trying to practice with the concept of immutability. I'm using the the spliceTest array as my main reference for creating copies of the array and mutating those. I'm coming to the problem when I declare removeOneItem variable, I somehow can't declare a new spread variable using the same reference of spliceTest.
const removeOneItem = [...spliceTest.splice(0,0), ...spliceTest.splice(1)];
const removeFive = [...spliceTest.splice(0,4), ...spliceTest.splice(5)];
const spreadTest = [...spliceTest];
console.log('removeOneItem:', removeOneItem)
console.log('spreadTest:', spreadTest, spliceTest)
console.log('removeFive:', removeFive)
Results::::::::::::
removeOneItem: [ 2, 3, 4, 5, 6, 7, 8, 9 ]
spreadTest: [] []
removeFive: [ 1 ]
According to MDN:
The splice() method changes the contents of an array by removing or
replacing existing elements and/or adding new elements in place.
This means, that the splice operation changes your array
Immutability of data is a cornerstone of functional programming and in general I'll do what you are trying to do: clone the data and mutate the clone. The following function takes an array and a series of sub-arrays. The sub-arrays consist of [startIndex, quantity]. It clones the original array by the spread operator and splices the clone according to the second parameter (...cutDeep). It will return an object with the original array and the cloned array. If you wrap everything in a function then your scope protects each return. Note on subsequent turns The second clone (secondResult.dissected) is spliced once more and the last log proves the original array is never mutated.
Demo
const data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f'];
const dissect = (array, ...cutDeep) => {
let clone = [...array];
for (let [cut, deep] of cutDeep) {
clone.splice(cut, deep);
}
return {
original: array,
dissected: clone
};
}
const firstResult = dissect(data, [2, 3], [5, 2], [9, 1]);
const secondResult = dissect(data, [3, 2], [10, 1]);
console.log(JSON.stringify(firstResult));
console.log(JSON.stringify(secondResult));
console.log(JSON.stringify(dissect(secondResult.dissected, [0, 2], [5, 1])));
console.log(JSON.stringify(data));
The problem is that you use splice when you most likely want to use slice.
splice is used for mutating an array, while slice is used to select a sub-array.
const sliceTest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// select a sub-array starting from index 1 (dropping 0)
const removeOneItem = sliceTest.slice(1);
// select a sub-array starting from index 5 (dropping 0, 1, 2, 3, and 4)
const removeFive = sliceTest.slice(5);
// spread the full array into a new one
const spreadTest = [...sliceTest];
// array log helpers (leave these out in your code)
const toString = array => "[" + array.join(",") + "]";
const log = (name, ...arrays) => console.log(name, ...arrays.map(toString));
log('removeOneItem:', removeOneItem)
log('spreadTest:', spreadTest, sliceTest)
log('removeFive:', removeFive)
slice already creates a shallow copy of the array, so [...arr.slice(i)] is not needed.

JS - For Loops Pushing Array

I have an initial array,
I've been trying to change values (orders) by using pop, splice methods inside a for loop and finally I push this array to the container array.
However every time initial array is values are pushed. When I wrote console.log(initial) before push method, I can see initial array has been changed but it is not pushed to the container.
I also tried to slow down the process by using settimeout for push method but this didnt work. It is not slowing down. I guess this code is invoked immediately
I would like to learn what is going on here ? Why I have this kind of problem and what is the solution to get rid of that.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial);
}
return schedulePattern;
}
**Console.log**
(4) [1, 2, 3, 4]
(4) [1, 4, 2, 3]
(4) [1, 3, 4, 2]
(3) [Array(4), Array(4), Array(4)]
0 : (4) [1, 3, 4, 2]
1 : (4) [1, 3, 4, 2]
2 : (4) [1, 3, 4, 2]
length : 3
When you push initial into schedulePattern, it's going to be a bunch of references to the same Array object. You can push a copy of the array instead if you want to preserve its current contents:
schedulePattern.push(initial.slice(0));
Good answer on reference types versus value types here: https://stackoverflow.com/a/13266769/119549
When you push the array to schedulepattern, you are passing a reference to it.
you have to "clone" the array.
use the slice function.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial.slice());
}
return schedulePattern;
}
​
You have to know that arrays are mutable objects. What does it mean? It means what is happening to you, you are copying the reference of the object and modifying it.
const array = [1,2,3]
const copy = array;
copy.push(4);
console.log(array); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3, 4]
There are a lot of methods in Javascript which provide you the way you are looking for. In other words, create a new array copy to work properly without modify the root.
const array = [1,2,3]
const copy = Array.from(array);
copy.push(4);
console.log(array); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
I encourage you to take a look at Array methods to increase your knowledge to take the best decision about using the different options you have.

Map/Set to maintain unique array of arrays, Javascript

I am trying to build unique array of arrays such that whenever I have new array to add it should only add if it doesn't already exist in collection
E.g. store all unique permutations of [1,1,2]
Actual : [[1,1,2],[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]
Expected : [[1,1,2],[1,2,1],[2,1,1]]
Approaches I tried:
Array.Filter: Doesn't work because arrays are object and each value in uniqueArrComparer is a unique object reference to that array element.
function uniqueArrComparer(value, index, self) {
return self.indexOf(value) === index;
}
result.filter(uniqueArrComparer)
Set/Map: Thought I can build a unique array set but it doesn't work because Set internally uses strict equality comparer (===), which will consider each array in this case as unique.
We cannot customize object equality for JavaScript Set
Store each array element as a string in a Set/Map/Array and build an array of unique strings. In the end build array of array using array of unique string. This approach will work but doesn't look like efficient solution.
Working solution using Set
let result = new Set();
// Store [1,1,2] as "1,1,2"
result.add(permutation.toString());
return Array.from(result)
.map(function(permutationStr) {
return permutationStr
.split(",")
.map(function(value) {
return parseInt(value, 10);
});
});
This problem is more of a learning exercise than any application problem.
One way would be to convert the arrays to JSON strings, then use a Set to get unique values, and convert back again
var arr = [
[1, 1, 2],
[1, 2, 1],
[1, 1, 2],
[1, 2, 1],
[2, 1, 1],
[2, 1, 1]
];
let set = new Set(arr.map(JSON.stringify));
let arr2 = Array.from(set).map(JSON.parse);
console.log(arr2)
If you are ok to use a library, try lodash uniqWith. This will recursively find groups of arrays OR objects with the comparator of your choice: equal in your case.
var arrayofarrays = [ [1,1,2], [1,2,1], [1,1,2], [1,2,1], [2,1,1], [2,1,1] ]
const uniqarray = _.uniqWith(arrayofarrays, _.isEqual);
console.log(uniqarray) //=> [[1, 1, 2], [1, 2, 1], [2, 1, 1]]
Bonus: it works on array of objects too
var objects = [{ 'x': 1, 'y': {b:1} }, { 'x': 1, 'y': {b:1} }, 
{ 'x': 2, 'y': {b:1} }, { 'x': 1, 'y': 2 } ];
const uniqarray = _.uniqWith(objects, _.isEqual);
console.log(uniqarray)
// => [{x: 1, y: {b: 1}}, {x: 2, y: {b: 1}}, {x: 1, y: 2}]
To get around the problem of each array being a unique object, you can stringify it so it's no longer unique, then map it back to an array later. This should do the trick:
var arr = [
[1, 1, 2],
[1, 2, 1],
[1, 1, 2],
[1, 2, 1],
[2, 1, 1],
[2, 1, 1]
];
var unique = arr.map(cur => JSON.stringify(cur))
.filter(function(curr, index, self) {
return self.indexOf(curr) == index;
})
.map(cur => JSON.parse(cur))
console.log(unique);
The fastest method I've found is:
const points = [
[0,0],
[100,100],
[400,400],
[200,200],
[200,200],
[200,200],
[300,300],
[400,400],
]
const uniquePoints = Array.from(
new Map(points.map((p) => [p.join(), p])).values()
)
All of the methods in this thread are fast. This one is faster than the Set method, however, as we never need to convert the stringified array back into a array.
To find unique objects, replace p.join() with JSON.stringify(p).
Note
In my case, the method shown above turned out to be the wrong strategy, as I was only really needing to check against identical adjacent points. For example, the test array used above includes the value [400,400] two times, though these values are not consecutive. The method shown above would have removed the second instance, while the code below would have kept it.
points = points.filter(
(point, i) =>
i === 0 ||
!(points[i - 1][0] === point[0] && points[i - 1][1] === point[1])
)
You can subclass Set for more flexibility in storing objects by storing the result of calling JSON.stringify on added objects.
class ObjectSet extends Set{
add(elem){
return super.add(typeof elem === 'object' ? JSON.stringify(elem) : elem);
}
has(elem){
return super.has(typeof elem === 'object' ? JSON.stringify(elem) : elem);
}
}
let set = new ObjectSet([[1,1,2],[1,2,1],[1,1,2],[1,2,1],[2,1,1],[2,1,1]]);
console.log([...set]);
console.log([...set].map(JSON.parse));//get objects back

Deep copy Javascript array with properties

Using .slice(), I can deep copy a Javascript Array of primitive types, for example:
var arr = [1,2,3,4];
var newArr = arr.slice();
newArr.push(5);
console.log(arr); // [1,2,3,4]
console.log(newArr); // [1,2,3,4,5]
However, If I add a property to arr like so:
arr.prop1 = 5;
and do the same:
var arr = [1,2,3,4];
arr.prop1 = 8;
arr.prop2 = 9;
var newArr = arr.slice();
newArr.push(5);
console.log(arr); // [1, 2, 3, 4, prop1: 9, prop2: 8]
console.log(newArr); // [1, 2, 3, 4, 5]
The property values do not carry over to newArr
I have considered not using .slice() and looping over the property values of arr instead, assigning them to newArr, using :
for (var key in arr) {
if (arr.hasOwnProperty(key)) {
newArr[key] = arr[key];
}
}
console.log(arr); // [1, 2, 3, 4, prop1: 9, prop2: 8]
console.log(newArr); // [1, 2, 3, 4, 5, prop1: 9, prop2: 8]
Is this going to be the quickest way to deep copy these arrays with properties? Is there in easier or cleaner way I can do this using .slice() or another array method? I am doing this operation tens of thousands of times and want it to be as clean and efficient as possible
You are trying to mix an array and object(to make an array behave like an object).
JavaScript array has numeric indexed items.
JavaScript object has string indexed items.
Arrays have a length property that tells how many items are in the array and is automatically updated when you add or remove items to the array.But ...
var arr = [1,2,3,4], newArr = arr.slice();
arr.prop1 = 7;
console.log(arr.length);
The output is 4, though you would expect it to be 5.But arr.prop1 = 7 does not actually add to the array.Setting a string parameter adds to the underlying object.
The length property is only modified when you add an item to the array, not the underlying object.The expression newArr = arr.slice() assigns a new array to newArr, so it remains an array and behaves like a pure array.
The property values do not carry over to newArr
If you still want to proceed using mixed numeric/string indexed sequences, cloning them, try using Object.assign() function. This should be the easiest way for your case:
The Object.assign() method is used to copy the values of all
enumerable own properties from one or more source objects to a target
object.
console.log(Object.assign(newArr, arr));
The output:
[1, 2, 3, 4, prop1: 7]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
How about using Object.create(proto)
var arr = [1,2,3,4];
arr.prop1 = 8;
arr.prop2 = 9;
var newArr = Object.create(arr);
newArr.prop1 = 12;
console.log(arr.prop1) // 8
console.log(newArr.prop1) // 12
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

pushing an array into another array

I have the following arrays.
var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3=[];
How would I push arr1 and arr2 into arr3 such that the result is:
arr3=[[1,2,3],[4,5,6]];
not
arr3=[1,2,3,4,5,6];
which is produced when using the .concat method.
var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3=[];
arr3.push(arr1,arr2);
console.log(arr3);
var arr1=[1,2,3];
var arr2=[4,5,6];
var arr3 = [arr1,arr2];
console.log(arr3);
arr3.push(arr1);
arr3.push(arr2);
will do the work.
Anyway just follow http://www.w3schools.com/jsref/jsref_push.asp for further clarifications.
Hope this helps.
You can use push() like in the other answers or this:
var arr1 = [1,2,3];
var arr2 = [4,5,6];
var arr3 = [arr1, arr2];
console.log(arr3);
As others users have posted, the solution at your problem is use .push() method, but i would try to give you more information about these two methods.
I suggest you to read this article, is very useful for me; I recap the main information for you below.
PUSH METHOD
Push method is used when you want add one or more elements, that were input arguments, to the array that invoked the method.
A simply example is the following:
var first_array = [1, 2, 3];
var result = first_array.push(4, 5, 6);
console.log(result); // 6
console.log(first_array ); // [1, 2, 3, 4, 5, 6]
The one thing is not immediately apparent is that it returns the length of the array after adding the new values, not the modified array. (check third line: console.log(result);)
CONCAT METHOD
While push alters the array that invoked it, concat returns a new array with the original array joined with the array/s or value/s that were provided as arguments.
There are a couple of the things to note about concat and how it creates the returned array.
Both strings and numbers are copied into the array, which means that they if the original value is changed, the value in the new array will be unaffected.
This is not true for objects.
Instead of copying objects into the new array, the references are copied instead. This means that if the values of objects change in one array, they will also be changed in the other array, as they are references to the objects not unique copies.
A simply example is the following:
var test = [1, 2, 3]; // [1, 2, 3]
var example = [{ test: 'test value'}, 'a', 'b', 4, 5];
var concatExample = test.concat(example); // [1, 2, 3, { test: 'test value'}, 'a', 'b', 4, 5]
example[0].test = 'a changed value';
console.log(concatExample[3].test); // Object { test: "a changed value"}
example[1] = 'dog';
console.log(concatExample[4]); // 'a'
I have created a fiddle for you with this example.
I hope that will be helpful

Categories

Resources