Why this rest parameter is usefull here? - javascript

I have two solutions for the same problem (both work), one is mine and the other one is from the internet.
In the internet's solution they add the variable modifiedArray. What is the point of doing that?
In the internet's solution, is the [...arr] in the modifiedArray variable not the same that the argument arr on the function removeFromArray()?
My solution:
const removeFromArray = function(arr, ...Args) {
for (i = 0; i <= Args.length; ++i) {
if (arr.includes(Args[i])) {
arr.splice(arr.indexOf(Args[i]), 1)
}
}
return arr;
}
module.exports = removeFromArray
Internet's solution:
const removeFromArray = function(arr, ...Args) {
let modifiedArray = [...arr]
for (i = 0; i < arr.length; ++i) {
if (modifiedArray.includes(Args[i])) {
modifiedArray.splice(modifiedArray.indexOf(Args[i]), 1)
}
}
return modifiedArray;
}
module.exports = removeFromArray
Test for both answers:
const removeFromArray = require('./removeFromArray')
describe('removeFromArray', function() {
it('removes a single value', function() {
expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
});
it('removes multiple values', function() {
expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
});
it('ignores non present values', function() {
expect(removeFromArray([1, 2, 3, 4], 7, "tacos")).toEqual([1, 2, 3, 4]);
});
it('ignores non present values, but still works', function() {
expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
});
it('can remove all values', function() {
expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4, 5)).toEqual([]);
});
it('works with strings', function() {
expect(removeFromArray(["hey", 2, 3, "ho"], "hey", 3)).toEqual([2, "ho"]);
});
it('only removes same type', function() {
expect(removeFromArray([1, 2, 3], "1", 3)).toEqual([1, 2]);
});
});

The difference between your code and the other one, is that yours changes the arr parameter directly whereas the other first makes a copy of the array, and then modifies that copy.
When you pass an array to a function, you are actually passing a reference to that array, not a copy of it. This means that when you modify arr directly, you are also modifying the source array.
Here is a nice example:
const removeFromArray1 = function(arr, ...Args) {
for (i = 0; i <= Args.length; ++i) {
if (arr.includes(Args[i])) {
arr.splice(arr.indexOf(Args[i]), 1)
}
}
return arr;
}
const removeFromArray2 = function(arr, ...Args) {
let modifiedArray = [...arr]
for (i = 0; i < arr.length; ++i) {
if (modifiedArray.includes(Args[i])) {
modifiedArray.splice(modifiedArray.indexOf(Args[i]), 1)
}
}
return modifiedArray;
}
const arr1 = [1, 2, 3, 4];
console.log('Returned array 1', removeFromArray1(arr1, 3));
console.log('Source array 1', arr1);
const arr2 = [1, 2, 3, 4];
console.log('Returned array 2', removeFromArray2(arr2, 3));
console.log('Source array 2', arr2);
Here you can see that arr1 is modified after calling removeFromArray1, but arr2 is not modified after calling removeFromArray2. Changing the source array might have odd side-effects if you try to use the initial array expecting it not to be changed.
let modifiedArray = [...arr] is a simple way to make a shallow copy of the array.

The second solution is nearer to functional programming pattern (pure function).
In functional programming you dont push or delete items into existing arrays or objects.
You would rather create a new array with all the same items as the original array, Then you modify the duplicate and return it. The duplication is done by
let modifiedArray = [...arr]
The concept is described as pure Function. A function should not change anything outside the function. No side effects.

Related

remove elements from an array javascript

this the forth project from the odin project, all tests passed but the fifth one which required removing all elements failed and when i run the code it returns an array with half elements in original array before mutating.
I don't know why IT DOESN'T RETURN AN EMPTY ARRAY.in the fifth test.
const removeFromArray = function (array, ...deleteElement) {
for (let i = 0; i < array.length; i++) {
if (array.includes(deleteElement[i])) {
array.splice(array.indexOf(deleteElement[i]), 1);
}
}
return array;
};
const randomArray = [1, 2, 3, 4];
console.log(removeFromArray(randomArray, 1, 2, 3, 4));
and this the test
const removeFromArray = require('./removeFromArray')
describe('removeFromArray', () => {
test('removes a single value', () => {
expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
});
test('removes multiple values', () => {
expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
});
test('ignores non present values', () => {
expect(removeFromArray([1, 2, 3, 4], 7, "tacos")).toEqual([1, 2, 3, 4]);
});
test('ignores non present values, but still works', () => {
expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
});
test.skip('can remove all values', () => {
expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)).toEqual([]);
});
test.skip('works with strings', () => {
expect(removeFromArray(["hey", 2, 3, "ho"], "hey", 3)).toEqual([2, "ho"]);
});
test.skip('only removes same type', () => {
expect(removeFromArray([1, 2, 3], "1", 3)).toEqual([1, 2]);
});
});
In the for you are looping trough the wrong element. It should be deleteElement.length, not array.length
const removeFromArray = function(array, ...deleteElement) {
for (let i = 0; i < deleteElement.length; i++) {
if (array.includes(deleteElement[i])) {
array.splice(array.indexOf(deleteElement[i]), 1);
}
}
return array;
};
const randomArray = [1, 2, 3, 4];
console.log(removeFromArray(randomArray, 1, 2, 3, 4));
EDIT:
although this will not work correctly when there are 2 same element to delete in the array. Here is another implementation which is deleting all matched indexes and not only the first one.
const removeFromArray = function(array, ...deleteElement) {
for (let i = 0; i < deleteElement.length; i++) {
let foundIndex = array.indexOf(deleteElement[i]);
while (foundIndex !== -1) {
array.splice(foundIndex, 1);
foundIndex = array.indexOf(deleteElement[i]);
}
}
return array;
};
const randomArray = [1, 2, 3, 1, 4];
console.log(removeFromArray(randomArray, 1));
Your function iterates over the wrong variable (array.length)
const removeFromArray = function (array, ...deleteElement) {
for (let element of deleteElement) {
if (array.includes(element)) {
array.splice(array.indexOf(element), 1);
}
}
return array;
};
The problem is that the for loop is using the array.length property dynamically, which means it is going to be evaluated every time you pass the loop, so since you are removing the elements from the original array, this value is not constant and at some point the array length value and the i counter will get out of sync.
You just need to save the array length value in a variable so it stays the same after you remove the objects from the array.
var arrLen = array.length;
for (let i = 0; i < arrLen; i++) {
...
}
Update:
Pasting the full code here plus a live sample:
https://jsfiddle.net/bcngr/sbk1yhmn/
const removeFromArray = function (array, ...deleteElement) {
var arrLen = array.length; // Save the length value before any modification to the array
for (let i = 0; i < arrLen; i++) { // use the variable instead of array.length
if (array.includes(deleteElement[i])) {
array.splice(array.indexOf(deleteElement[i]), 1);
}
}
return array;
};
const randomArray = [1, 2, 3, 4];
console.log(removeFromArray(randomArray, 1, 2, 3, 4));

how to remove following arguments after the array

destroyer(array1, some arguments) function should return the array1 excluding the arguments. I found some working ways like return arr = arr.filter(val => !rem.includes(val)); but I need to fix this code and find out why this code giving an incorrect result. It supposed to be [1]
function destroyer(arr, ...rem) {
for(let i = 0; i < arr.length; i++) {
if (rem.includes(arr[i])) {
arr.splice(i, 1);
};
};
return arr;
}
console.log(destroyer([3, 5, 1, 2, 2], 2, 3, 5));
function destroyer(arr, ...rem) {
const itemsToRemove = new Set(rem);
return arr.reduce((acc, curr) => itemsToRemove.has(curr) ? acc : [...acc, curr] ,[])
}
console.log(destroyer([3, 5, 1, 2, 2], 2, 3, 5));
The problem is the call of the function Array.prototype.splice within the for loop, this function is mutating the array and basically affects the indexes of the array, therefore the current index i is no longer valid.
To work around this, you can loop in a backward direction, this way we can mutate the array, and the current index is not affected.
One more thing, your approach is mutating the array, a better approach would be by using the function Array.prototype.filter along with the function Array.prototype.includes, see other answers.
function destroyer(arr, ...rem) {
for(let i = arr.length; i >= 0; i--) {
if (rem.includes(arr[i])) {
arr.splice(i, 1);
};
};
return arr;
}
console.log(destroyer([3, 5, 1, 2, 2], 2, 3, 5));
Or you can do it like this:
const destroyer=(arr, ...rem)=>arr.filter(v=>!rem.includes(v));
console.log(destroyer([3, 5, 1, 2, 2], 2, 3, 5));

Fundamentals 4 Odin removeFromArray syntax questions?

const removeFromArray = function(firstArray,...toRemove) {
let modifiedArray = [...firstArray];
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
This is a project from odin fundamentals 4 for reference. I accidentally found the solution in my impatience while trying to figure out how to do it. The goal of said function is to input an array of whatever you like and remove whatever you like from said array via a function with 2 parameters. Ie I input removeFromArray([3,4,5], 3) and will be returned [4,5]
Question 1) Why are the brackets necessary around [...firstArray]
Question 2) If [i] is being used to loop over how many iterations you decide to input for the first parameter (an array), why is it being attached to (toRemove[i])?
Maybe I have the wrong idea of how this is actually working as a whole? Help appreciated, thanks!
Tests:
const removeFromArray = require('./removeFromArray')
describe('removeFromArray', function() {
it('removes a single value', function() {
expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
});
it('removes multiple values', function() {
expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
});
it('ignores non present values', function() {
expect(removeFromArray([1, 2, 3, 4], 7, "tacos")).toEqual([1, 2, 3, 4]);
});
it('ignores non present values, but still works', function() {
expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
});
it('can remove all values', function() {
expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)).toEqual([]);
});
it('works with strings', function() {
expect(removeFromArray(["hey", 2, 3, "ho"], "hey", 3)).toEqual([2, "ho"]);
});
it('only removes same type', function() {
expect(removeFromArray([1, 2, 3], "1", 3)).toEqual([1, 2]);
});
});
const removeFromArray = function(firstArray,...toRemove) {
let modifiedArray = [...firstArray];
for (let i = 0; i < toRemove.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
};
console.log(removeFromArray([3,4,5], 3,5))
Question 1) Why are the brackets necessary around [...firstArray]
Because without them it's a syntax error. That form of spread notation is only valid within an array literal. (Objects have the same notation, but it means something different.) ... isn't an operator, it's syntax that's only defined in certain places (array literals, object literals, destructuring patterns, function argument lists, and function parameter lists).
Question 2) If [i] is being used to loop over how many iterations you decide to input for the first parameter (an array), why is it being attached to (toRemove[i])?
It shouldn't be, that's incorrect. It happens to work if the array is longer than the number of things you're trying to remove from it because you're going from 0 to < firstArray.length and so if that's as many or more than the number of entries in toRemove, you see all of toRemove's entries (and undefineds, if firstArray is longer). Here's an example of that code not working:
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = [...firstArray];
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
console.log(removeFromArray([1, 2], 3, 4, 1)); // Should be [2], is actually [1, 2]
Instead, the for loop should be through < toRemove.length.
Another issue with that code is that it never declares i, so it's falling prey to what I call The Horror of Implicit Globals. I recommend using strict mode to make that the error it always should have been:
"use strict";
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = [...firstArray];
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
console.log(removeFromArray([1, 2], 3, 4, 1)); // Should be [2], is actually [1, 2]

Why isn't my javascript iterating through all of the arguments?

I'm working on The Odin Project and am on the Fundamentals Part 4 "removeFromArray" assignment. I have to return an array having removed all of the elements that are in the list of arguments. It works with two arguments, but I can't get it to work with four. My code is below:
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = firstArray;
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
removeFromArray([1, 2, 3, 4], 7, 2) // works, returns [1, 3, 4]
removeFromArray([1, 2, 3, 4], 3, 2) // also works, returns [1, 4]
removeFromArray([1, 2, 3, 4], 1, 2, 3, 4) //does not work, returns [3, 4]
Any help is much appreciated.
Splicing from the array shifts all the remaining elements down by one, so you end up skipping over the next element. I'd recommend using Array.prototype.filter instead.
I think the real issue here, is that you may not be aware that arrays, objects, and functions in JavaScript are passed by reference.
let modifiedArray = firstArray;
Meaning your firstArray and modifiedArray are both pointing to the same array in memory. The code above assigns the EXACT same array, technically the address in memory of the firstArray to the modifiedArray. Therefore, as you remove items from the modifiedArray, you are also removing them from the firstArray, and therefore changing the length of the firstArray.
You need to copy the array by value, not by reference.
Solution:
Therefore changing
let modifiedArray = firstArray;
to
let modifiedArray = [...firstArray];
or
let modifiedArray = firstArray.slice();
The first solution leverages destructuring of the first array, to create a copy of the array by value, not pointing to the same array in memory.
The second may be more familiar to you as a beginner, since this simply returns a copy of the array, without removing any elements.
See this thread if you have more questions about copying arrays by value: Copy array by value
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = [...firstArray];
for (i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
console.log(removeFromArray([1, 2, 3, 4], 7, 2)); // works, returns [1, 3, 4]
console.log(removeFromArray([1, 2, 3, 4], 3, 2)); // also works, returns [1, 4]
console.log(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)); //does not work, returns [3, 4]
1, 2, 3, 4
After you remove the first item, everything suffles down, so what was the second item becomes the first item.
You don't have that problem if you start at the end and work in reverse (sort the list of items to remove).
I've changed the following line:
let modifiedArray = firstArray;
to
let modifiedArray = [...firstArray];
The problem you were facing is you were iterating over an array assuming it is a copy of the firstArray. But it was just pointing to firstArray. Now in the loop you startet removing items from the array and after you removed 2 in the iteration you were already on 2 so there was nothing left to iterate over...
const removeFromArray = function(firstArray, ...toRemove) {
let modifiedArray = [...firstArray];
for (var i = 0; i < firstArray.length; i++) {
if (modifiedArray.includes(toRemove[i])) {
modifiedArray.splice(modifiedArray.indexOf(toRemove[i]), 1)
}
}
return modifiedArray;
}
var x = removeFromArray([1, 2, 3, 4], 7, 2);
var y = removeFromArray([1, 2, 3, 4], 3, 2);
var z = removeFromArray([1, 2, 3, 4], 1, 2, 3, 4);
console.log('x', x);
console.log('y', y);
console.log('z', z);
You take the same index for the array and for the items to remove, which may not be the same.
You could iterate the item to remove and get the index and splice this item from firstArray.
This approach muates the given array and keeps the same object reference.
const removeFromArray = function(firstArray, ...toRemove) {
for (let i = 0; i < toRemove.length; i++) {
let index = firstArray.indexOf(toRemove[i]);
if (index === -1) continue;
firstArray.splice(index, 1);
}
return firstArray;
}
console.log(...removeFromArray([1, 2, 3, 4], 7, 2)); // [1, 3, 4]
console.log(...removeFromArray([1, 2, 3, 4], 3, 2)); // [1, 4]
console.log(...removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)); // []
const removeFromArray = (firstArray, ...toRemove) =>
firstArray.filter(e => !toRemove.includes(e));
console.log(removeFromArray([1, 2, 3, 4], 7, 2));

Custom method to duplicate an array in javascript

How in javascript make a function for array, which will work like this:
const a = [1, 2, 3, 4, 5];
a.duplicate(); // 1, 2, 3, 4, 5, 1, 2, 3, 4, 5
Try following, using Array.push
const a = [1, 2, 3, 4, 5];
a.push(...a);
console.log(a);
Or can add a prototype function (Mutates Original)
const a = [1, 2, 3, 4, 5];
Array.prototype.duplicate = function(){
this.push(...this); // mutates the original array
}
a.duplicate();
console.log(a);
Or can add a prototype function (Creates New)
const a = [1, 2, 3, 4, 5];
Array.prototype.duplicate = function(){
return [...this, ...this]; // creates new
}
console.log(a.duplicate()); // duplicated array
console.log(a); // no change
Add a new function into the Array.prototype with name duplicate.
If you want to return a new array
Array.prototype.duplicate = function () {
return [...this, ...this];
}
const a = [1, 2, 3, 4, 5];
const b = a.duplicate();
console.log(b);
Or mutate the original one
Array.prototype.duplicate = function () {
this.splice(0,0, ...this);
}
const a = [1, 2, 3, 4, 5];
a.duplicate();
console.log(a);
Or you can use [...a,...a] to get new array without modifying the original array
const a = [1, 2, 3, 4, 5];
Array.prototype.duplicate = function(){
return [...this,...this]
}
console.log(a.duplicate())
console.log("Orignal", a)
In case you don't know what ... is, It's called spread syntax
Create a new prototype as below:
Array.prototype.duplicate = function() {
var length = this.length;
for (var i = 0; i < length; i++)
this.push(this[i]);
}
You can use this code -
function duplicate(coll) {
return coll.concat(coll);
}
duplicate([1, 2]);
You can define a function on array by adding to its prototype (as commented by Teemu).
Your requirements are not very clear, but the following will return an array as in your example:
Array.prototype.duplicate = function() {
return this.concat(this);
}

Categories

Resources