I have an array with a fixed length of 6.
I want to remove the first element from the array, then shift all the elements left by 1, but the length of the array should remain as 6 (the 6th position of the array can be undefined)
I tried using splice but the array length was reduced to 5 which is not what I want to happen.
What is the best approach to achieve the above?
You can use Array.prototype.slice (which does not mutate the original array unlike splice) to take a copy of the array from the 1st position.
Then use Array.from to get a new array from the copy and mention the length property value of the old array:
const arr = [1, 2, 3, 4, 5, 6]
const newArr = Array.from({length: arr.length, ...arr.slice(1)});
console.log(newArr);
You can shift the first element, then push undefined, as shown below:
const array = [1, 2, 3, 4, 5, 6]
array.shift()
array.push(undefined)
console.log(array)
Related
Background knowledge
We can mutate an array using splice and filter. filter is not a mutable method, but splice is a mutable method. So if we use splice to an array, then the original array will be mutated.
The problem
Now, we are going to do forEach to an array and if a condition matches, we will remove an element using splice and filter. Let's see:
1. splice
let arr = [1, 2, 3, 4];
arr.forEach(el => {
console.log(el);
if (el === 2) {
arr.splice(el, 1); // remove element 2
}
});
Yeah that's what we expect. That prints 1, 2, 4. Because in the middle of the loop we mutate the arr, the result is broken.
2. filter
let arr = [1,2,3,4];
arr.forEach(el => {
console.log(el);
if (el === 2) {
arr = arr.filter(el => el !== 2); // remove element 2
}
});
However, as you can see, that prints 1, 2, 3, 4 even if we mutate the arr in the middle of the loop!
The question
We've mutated two arrays in the middle of the loop in a similar way, but the results are different! What happened?
Why does splice affect original array but filter not?
That is simply how Array.prototype.splice, Array.prototype.filter and Array.prototype.forEach are defined. splice is meant to modify the array (and return removed values) while filter is meant to return a copy of the array with only the matching values
Array.prototype.forEach iterates over the array in ascending index order. Even though you modify the array during e.g. index 3, next iteration it'll just go to the next index, e.g. 4, unless the end of the array is already reached. Mind that once .forEach is called on an array, it'll keep working on that array. You setting arr to another value doesn't affect the array nor the state of the .forEach call. Similar to a regular function call:
let someVariable = 5;
function test(param) {
someVariable = 6;
console.log(param); // still prints 5
}
test(someVariable);
After all, JavaScript works by reference (or primitive values), you're not passing a pointer to the someVariable variable like you can do in some other languages.
One point of clarification: in the OP's first example he's actually deleting the third value in the array rather than the second one despite the comment indicating he meant to delete the second element (at least, that is what I think he was going for based on the subsequent example).
One would fix that problem by using the second parameter passed to the forEach callback as follows:
let arr = [1, 2, 3, 4];
arr.forEach((el, index) => {
console.log(el);
if (el === 2) {
// actually remove the second element instead of the element at index 2
arr.splice(index, 1);
}
});
What I find interesting is that even if the semantic error is fixed, the console will still show what the OP mentioned:
1
2
4
This, despite the resulting value of arr being set to [1, 3, 4] by the splice() call.
What happened? The MDN has a similar example regarding modifying an array inside a forEach loop. Basically, the callback one passes to forEach is invoked for every index of the list until it reaches the length of the list. In the second invocation, the underlying logic is pointing to index 1 and the callback deletes that index, moving everything currently following that index forward one index in the array: the value 3 is moved to index 1 and the value 4 is moved to index 2. Because we've already iterated over index 1, the third invocation will be invoked on index 2 which now contains the value 4.
The following table is another way to see this:
Iteration
value of el
arr value before callback
arr value after callback
1
1
[1, 2, 3, 4]
[1, 2, 3, 4]
2
2
[1, 2, 3, 4]
[1, 3, 4]
3
4
[1, 3, 4]
[1, 3, 4]
Basically, you can think of Array.prototype.forEach being defined similarly to the following:
Array.prototype.forEach = function(callbackFn, thisArg) {
for (let index = 0; index < this.length; ++index) {
callbackFn.call(thisArg, this.at(index), index, this)
}
}
The difference between the two examples is that in the first one, the OP is using splice to modify the object referenced by the variable arr "in place" (as noted in the MDN doc). In the second example, the OP is changing the variable arr to point to a new object; however, because forEach is a function, the original object referenced by arr will be kept in scope by the forEach closure until the function completes. This becomes a little easier to see when you add a little more logging to the example using the third parameter passed to the callback (the array against which the callback was executed).
let arr = [1,2,3,4];
arr.forEach((el, index, list) => {
console.log("el:", el, "list:", list, "arr:", arr);
if (el === 2) {
arr = arr.filter(el => el !== 2); // remove element 2
}
});
This modified example will produce the following output:
el: 1 list: [1, 2, 3, 4] arr: [1, 2, 3, 4]
el: 2 list: [1, 2, 3, 4] arr: [1, 2, 3, 4]
el: 3 list: [1, 2, 3, 4] arr: [1, 3, 4]
el: 4 list: [1, 2, 3, 4] arr: [1, 3, 4]
One can see that the value of list, which comes from the forEach closure, never changes despite arr getting overwritten during the second iteration.
I have two arrays
[10,8,0,5,3]
[2,4,1,1,3]
I would like to sort the first array to decreasing order, so it becomes [10,8,5,3,0]. The first array's original indices [0, 1, 2, 3, 4] after sorted becomes [0, 1, 3, 4, 2] for example, the original number 5 was in 3rd index, after sorted, it comes to the 2 index. Now, I would like to apply the sorted index to the second array [2,4,1,1,3], so it becomes [2, 4, 1, 3, 1] Is there a quick and easy way to do it?
I came up with 2 solutions so far:
construct an object that holds the key value pair, with the keys being the values of the first array, and values are the second array, for example
{'10': 2, '8': 4, '0': 1, '5': 1, '3':3 }`
Then sort the object according to the key values. Then the values will be sorted, then retrieve all the values from the object.
Construct a map with the values of the first array pointing to each of the values of the second array. Sort the map, and retrieve each value.
These two methods seem a bit complicated. I am wondering if there is an easier way such as leverage the array.sort() function
If you zip the arrays together into a single array, where each item contains the ith value from both arrays, you can sort that larger array pretty easily:
const arr1 = [10,8,0,5,3];
const arr2 = [2,4,1,1,3];
const zipped = arr1.map((num, i) => [num, arr2[i]]);
zipped.sort((a, b) => b[0] - a[0]);
console.log(zipped.map(arr => arr[0]));
console.log(zipped.map(arr => arr[1]));
I want to iterate over an array in reverse order using Lodash. Is that possible?
My code looks like below.
var array = [1, 2, 3, 4, 5];
_.each(array, function(i) {
_.remove(array, i);
});
When I do _.pullAt(array, 0) new array is [2, 3, 4, 5]. All array elements shifted to left by 1 position, and current index is pointing to element 3. After next iteration, 3 will get deleted and then 5. After 3rd iteration array contains [2, 4] which I couldn't delete.
If I iterate over the array in reverse order, then this problem won't arise. In Lodash, can I iterate over an array in reverse order?
You can use _.reverse available in version 4:
var array = [1, 2, 3, 4, 5];
array = _.reverse(array)
console.log(array)
//5, 4, 3, 2, 1
See How do I empty an array in JavaScript? if you only want that and choose your weapon.
Otherwise, strictly answering the question, to iterate the indices from the length of the array to zero, you could use the "down-to" --> operator1. But that's not really necessary, even underscore isn't necessary in this case as the .pop function is enough.
var arr = [1, 2, 3, 4, 5],
index = arr.length;
while (index --> 0) {
console.log(index);
arr.pop();
}
console.log(arr);
If you're using Lodash like the functions referenced in the question seem to indicate, you could use _.eachRight.
var arr = [1, 2, 3, 4, 5];
_.eachRight(arr, function(value) {
console.log(value);
arr.pop();
});
console.log(arr);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
1 The down-to operator doesn't exist and is only a -- decrement followed by a > comparison.
I'm trying to compare the elements of an array(Array1) to the positions of the other array(Array2) and generate a new array(Array3) which contains the elements of Array2 whose positions are equal to the elements of Array1.
var Array1 = [1, 2, 6] // Dynamic array, maxlength= 6
var Array2 = [6, 8, 9, 3, 5, 2] // dynamic array of length=6
var Array3 = [6, 8, 2] // note that positions are form 1-6 and not 0-5
How can I achieve this?
The solution to your problem is
Array1.map(function(key) {return Array2[key - 1];});
The use of key - 1 has to do with the fact that you are denoting the first element of Array2 as position "1".
If instead you followed the more popular programming convention that the first element of Array2 is position "0" (or index 0), then the solution would be
Array1.map(function(key) {return Array2[key];});
I have an Array like
var myArray = new Array;
I have to push some elements to array in such a way that the elements will be replaced with same index.
Example :
myArray.push(1);
myArray.push(2);
myArray.push(3);
so now
myArray[0] = 1
myArray[1] = 2
now when i will push element 3 then
myArray[0] will be replaced with 3 and myArray[1] will be replaced with 1 and the element 2 will be removed.
It will continue according to the number of elements pushed...
Can any body help me with this requirement...
push adds to the end of an array. If you want to add a value to the beginning of an array you can use unshift.
myArray.unshift(3);
You can then use pop to remove the last element:
arr.pop();
DEMO
However, what you might need, given that you need to remove the same number of elements from an array that you add is a function that uses concat and slice instead:
function pusher(arr, add) {
return add.concat(arr).slice(0, arr.length);
}
var arr = [1, 2, 3, 4];
var arr = pusher(arr, [5, 6]); // [5, 6, 1, 2]
DEMO
I think you need something in the lines of:
myArray.unshift(element);
myArray.pop();
Explanation:
unshift: inserts the element on position 0 and moves all other elements one position to the right
pop: removes last element from array