I've been trying to write a function which takes in an array as the first argument, then one or more other arguments which are numbers. The purpose of the function is to check whether these numbers are present in the array and remove them if so.
I have tried the following but the results haven't been what I had expected.
The desired outcome is that 3 and 2 be removed from the array leaving me with [1,4]. Instead, only 2 is removed with the end result being [1,3,4]. I've been struggling with this for a while and would appreciate any feedback you might be able to provide. I'm knew to this and this is the first problem which has left me stumped so far!
function test(myArray, ...checkNums) {
for (let num in checkNums) {
for (let num2 in myArray) {
if (myArray[num] == checkNums[num2]) {
myArray.splice(num, 1);
}
}
}
return myArray;
}
const arr = test([1, 2, 3, 4], 3, 2);
console.log({arr})
The easiest way is to just filter the array to only keep values not in checkNums. Using a Set gives better performance (depending on the implementation, lookup is either O(1) or O(log n) or anything sublinear for a Set, compared to O(n) for an Array).
function test(myArray, ...checkNums) {
const checkNumsSet = new Set(checkNums);
return myArray.filter((num) => !checkNumsSet.has(num));
}
const arr = test([1, 2, 3, 4], 3, 2);
console.log({arr})
With myArray and checkNums as arrays, you can use a filter based on .includes:
const myArray = [1,2,3,4];
const checkNums = [3,4];
const filterNums = (nums, checkNums) => {
return nums.filter(num => !checkNums.includes(num));
}
console.log(filterNums(myArray, checkNums));
Your code is removing items so your index variable is stale after you remove an element. The simplest fix is to just iterate backwards.
Also, you should avoid using for in to iterate over an array
Lastly, your array was just modifying what was passed in but you never kept a reference to it, I'm returning the modified array.
function test(myArray, ...checkNums) {
for (let checkNumsIndex = checkNums.length - 1; checkNumsIndex >=0; checkNumsIndex--) {
for (let myArrayIndex = myArray.length - 1; myArrayIndex >=0; myArrayIndex--) {
if (myArray[myArrayIndex] == checkNums[checkNumsIndex]) {
myArray.splice(myArrayIndex, 1);
}
}
}
return myArray;
}
const arr = test([1, 2, 3, 4], 3, 2);
console.log({arr});
A more straight forward is using filter and includes. This doesn't have the problem that your example has where you're testing values outside of the bounds of the array.
function removeElements(myArray, ...checkNums) {
return myArray.filter((num) => !checkNums.includes(num));
}
const arr = removeElements([1, 2, 3, 4], 3, 2);
console.log({arr});
You can use for...of see for..in vs for...of in order to iterate through your arguments, check if the number exist in your array and if yes, splice at index number
function test(myArray, ...checkNums) {
//get the elements from ...checkNums
for (let num of checkNums) {
//check if your number exist in array
if (myArray.includes(num)) {
const indexOfNum = myArray.indexOf(num);
//if it exists splice at found index of your umber
myArray.splice(indexOfNum, 1)
}
}
return myArray;
}
const result = test([1, 2, 3, 4], 3, 2);
console.log(result)
Related
I'm trying to build a function that removes an item from an array. Both the array and item are configured using parameters pushing in when I call the function.
However it's not returning the expected [1,2,4] rather it's returning "not yet" a string I built into an if statment to return if it fails.
I can see in a console log the popped variable = 3 and the current for loop is correctly looping through all the options. So why isn't it working?
const removeFromArray = function() {
let args = Array.from(arguments);
let popped = args.pop();
for (i = 0; i < args.length; i++) {
let current = args[i];
if (current === popped) {
console.log(args);
return args;
} else {
console.log("not yet");
}
}
};
removeFromArray([1, 2, 3, 4], 3);
Ok I've commented your code, the problems in it and made changes accordingly so that it works like you wanted it to:
const removeFromArray = function()
{
// arguments is not [1, 2, 3, 4, 3], but instead it's [[1, 2, 3, 4], 3] (length is 2, remember this later)
let args = Array.from(arguments);
// pop works correctly and returns 3
let popped = args.pop();
// here we cannot loop with args.length, as it is 2
// if we change args.length to args[0].length, this will work
for (i = 0; i < args[0].length; i++) {
// args[i] won't work here for the same reason args.length didn't work,
// because we're targeting a wrong thing
// if we change this to args[0][i], it will work
let current = args[0][i];
// After the changes, this if will work correctly
if (current === popped) {
// We can't just return args
// A) we're once again targeting and wrong thing
// B) we haven't removed anything yet
// so lets change this to first splice the array (remove the wanted value)
args[0].splice(i, 1);
// and then return the array where the wanted value is removed
return args[0];
}
}
};
const newArray = removeFromArray([1, 2, 3, 4], 3);
// output the returned new array where 3 is removed
console.log(newArray)
The main problem is that args does not contain what you thought it does (the numbers array), it is actually args[0] that does.
The other thing was that when you found the value you wanted to remove from the array, you never actually removed it. So here we use splice to actually remove the value before returning.
const removeFromArray = function (array, itemToRemove) {
return array.filter(item => item !== itemToRemove);
};
I don't know why you don't use any build-in function of JS like
let removeFromArray = (arr, remove) => arr.filter(x => x != remove)
let filteredArray = removeFromArray([1, 2, 3, 4], 3)
But let's do it your way
const removeFromArray(arr, remove) {
const items = [];
for (const item of arr) {
if (item != remove) items.push(item)
}
return items;
};
removeFromArray([1, 2, 3, 4], 3);
This is how I would do it, since you said you wanted to modify the original array rather than create a new one, splice would be the correct tool to use.
function removeFromArray(arr, rem){
while(~arr.indexOf(rem)){
arr.splice(arr.indexOf(rem), 1);
}
}
var arr = [1, 2, 3, 4, 3];
removeFromArray(arr, 3)
console.log(arr);
The link to this challenge:
https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/basic-algorithm-scripting/slice-and-splice
I didn’t know how to use splice() so I used the other way to solve the problem.
what did I do wrong here?
function frankenSplice(arr1, arr2, n) {
let result = [];
let array1 = arr1.toString();
let array2 = arr2.toString();
for (let i = 0; i<array1.length; i++){
for (let j = 0; j<array2.length; j++){
if (array1[j] == n){
return result += array2[j] + array1 + array2.slice(1);
}
}
}
return result;
}
console.log(frankenSplice([1, 2, 3], [4, 5], 1));
It should return [4, 1, 2, 3, 5], however my code returns [41,2,3,5].
I wanted to use arr1.split() so there will have a comma between 4 and 1, but it says arr1.split is not a function.
I feel like I am so close to the answer but also feel that my logic could be totally wrong.
Please help, thank you!
First line uses the spread syntax to copy the elements of the array into another array, since the original orray should not be altered, according ot the rules.
The second line uses splice, and starting at the nth index, removes 0 items from the array, and then copies all the items from array1 into it.
Then we returned the copied array.
function frankenSplice(arr1, arr2, n) {
var a = [...arr2]
a.splice(n, 0, ...arr1);
return a;
}
I'm a newbie to all of this and trying to improve myself by solving problems and challenges.
I came across a problem whereby I have an unordered array which contains 8 integers.
eg [2,3,1,4,6,5,8,7]
I need to sort it [1,2,3,4,5,6,7,8] and reorder the array so that the array starts with the end value and then the first value and so on eg [8,1,7,2,6,3,5,4,]
I worked out I could use map() to iterate across the array and then use push() with pop() and shift() however it leaves the last 2 numbers behind in the original array and I'm not sure why. I got around this by using a concat and a reverse but I still don't understand why pop and shift don't bring across all the elements.
Code below that doesn't pull all the elements:
const reorder = (array) => {
let store = []
array.sort((a, b) => a - b).map((item, i) => {
if (array) {
store.push(array.pop())
store.push(array.shift())
}
})
return store
}
reorder([2, 3, 1, 4, 6, 5, 8, 7]) // returns [8,1,7,2,6,3]
Code that works but I have to add a concat and a reverse:
const reorder = (array) => {
let store = []
array.sort((a, b) => a - b).map((item, i) => {
if (array) {
store.push(array.pop())
store.push(array.shift())
}
})
return store.concat(array.reverse())
}
reorder([2, 3, 1, 4, 6, 5, 8, 7]) //returns [8,1,7,2,6,3,5,4]
Thanks for any help
I would just bisect the array, sort them in opposite orders and then add each element from each array to a new array
Given that you want to then take the sorted bisected arrays and produce another single array, I'd then use Array.prototype.reduce:
const alternatingSort = function (array) {
array = array.sort();
const midpoint = Math.round(array.length / 2)
let arr1 = array.slice(0, midpoint);
let arr2 = array.slice(midpoint);
arr2 = arr2.sort(function (a, b) { return b - a });
return arr1.reduce(function (retVal, item, index) {
arr2[index] && retVal.push(arr2[index]);
retVal.push(item);
return retVal;
}, []);
}
console.log(alternatingSort([2, 3, 1, 4, 6, 5, 8, 7]));
console.log(alternatingSort([2, 3, 1, 4, 6, 5, 8])); // with odd number
As I've seen nobody explained why the original OP solution doesn't work, Here is why:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/
Map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values (including undefined).
It is not called for missing elements of the array; that is:
1.Indexes that have never been set;
2.which have been deleted; or
3.which have never been assigned a value.
So what is happening in our code is that:
On the first iteration,
[(2), 3, 1, 4, 6, 5, 8, 7]
Map picks the first element(2) in the array, and delete the first and last characters in the array, so the array becomes
[3,(1), 4, 6, 5, 8]
Now, as map will not consider deleted elements, the second element(1) in the current array is called, also the first and last element in also removed:
[1, 4,(6), 5]
Now, map is trying to find the third element(6), and delete the first and last element:
[4,6]
Now, map is trying to find the fourth element, which is out of bound, so the map function will terminate.
So, you are strongly advised not to use Array.prototype.shift or Array.prototype.pop in Array.prototype.map.
You can do it following way:
const reorder = (array) => {
array.sort((a, b) => a - b);
const result = [];
const length = array.length;
for (let i = 0; i < length; i++) {
if (i % 2 === 0) {
result.push(array.pop());
} else {
result.push(array.shift());
}
}
return result;
}
const result = reorder([2, 3, 1, 4, 6, 5, 7]);
console.log(result);
Notice that I've intentionally made the array length to be an odd number. Some of the solutions here will break if the length is an odd number.
Personally I would sort, split in half and then just insert in. Not very fancy, but gets the job done.
function strangeWeave (arr) {
var sorted = arr.slice().sort()
var result = sorted.splice(0,Math.floor(sorted.length/2))
for (let i=0;sorted.length;i+=2) {
result.splice(i,0,sorted.pop())
}
return result
}
console.log(strangeWeave([1,2]))
console.log(strangeWeave([1,2,3]))
console.log(strangeWeave([1,2,3,4,5,6,7,8]))
console.log(strangeWeave([1,2,3,4,5,6,7,8,9]))
There is a much easier solution to sort two different arrays, one normal and one in reverse, then connect them together. Here is the code for that:
var myArray = [1, 3, 2, 4, 5, 7, 6, 8];
function getCopy(arr) {
var x = [];
for(var i = 0; i < arr.length; i++)
x.push(arr[i]);
return x;
}
function sortMyWay(arr) {
var sortedArr = [],
leftSide = getCopy(arr).sort()
.splice(0, Math.ceil(arr.length / 2)),
rightSide = getCopy(arr).sort().reverse()
.splice(0, Math.floor(arr.length / 2));
for(var i = 0; i < arr.length; i++)
i % 2
? sortedArr.push(leftSide[Math.floor(i / 2)])
: sortedArr.push(rightSide[Math.floor(i / 2)]);
console.log(sortedArr);
return sortedArr;
}
var sortedArr = sortMyWay(myArray);
Hope it helped!
Happy coding :)
Why does a return of the push method cause
Uncaught TypeError: acc.push is not a function
But a return concat results in the correct solution?
[1, 2, 3, 4].reduce(function name(acc, curr) {
if (even(curr)) {
return acc.push(curr);
}
return acc;
}, []);
function even(number) {
if (number % 2 === 0) {
return true;
}
return false;
}
[1, 2, 3, 4].reduce(function name(acc, curr) {
if (even(curr)) {
return acc.concat(curr);
}
return acc;
}, []);
function even(number) {
if (number % 2 === 0) {
return true;
}
return false;
}
The push() adds elements to the end of an array and returns the new length of the array. Thus your return here is invalid.
The concat() method is used to merge arrays. Concat does not change the existing arrays, but instead returns a new array.
Better to filter, if you want a NEW array like so:
var arr = [1, 2, 3, 4];
var filtered = arr.filter(function(element, index, array) {
return (index % 2 === 0);
});
Note that assumes the array arr is complete with no gaps - all even indexed values. If you need each individual, use the element instead of index
var arr = [1, 2, 3, 4];
var filtered = arr.filter(function(element, index, array) {
return (element% 2 === 0);
});
According to the MDN document say that:
push() method: adds one or more elements to the end of an array and returns the new length of the array.
const count = ['pigs', 'goats'].push('cows');
console.log(count); // expected output: 3
concat() method is used to merge two or more arrays. This method does not change the existing arrays but instead returns a new array
console.log(['a'].concat(['b']));// expected output: Array ["a", "b"]
And combined with the final Array#reduce's parameter is the array initialize []), which means that you want to return an array result.
==> So that's the reason why in case that you use concat working well.
Refactor code
If you still want to use Array#reduce and Array#push
const even = (number) => number%2 === 0;
const result = [1, 2, 3, 4].reduce(function name(acc, curr) {
if(even(curr)) acc.push(curr); // Just add one more item instead of return
return acc;
}, []);
console.log(result);
The simpler way is to use Array#filter
const even = (number) => number%2 === 0;
console.log([1, 2, 3, 4].filter(even));
acc should not be an array. Look at the documentation. It can be one, but..
It makes no sense at all to reduce an array to an array. What you want is filter. I mean, reduce using an array as the accumulator and concating each element to it technically does work, but it is just not the right approach.
var res = [1, 2, 3, 4].filter(even);
console.log(res);
function even(number) {
return (number % 2 === 0);
}
https://dev.to/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki
Concat is 945x slower than push only because it has to create a new array.
Why does a return of the push method cause
Uncaught TypeError: acc.push is not a function
But a return concat results in the correct solution?
[1, 2, 3, 4].reduce(function name(acc, curr) {
if (even(curr)) {
return acc.push(curr);
}
return acc;
}, []);
function even(number) {
if (number % 2 === 0) {
return true;
}
return false;
}
[1, 2, 3, 4].reduce(function name(acc, curr) {
if (even(curr)) {
return acc.concat(curr);
}
return acc;
}, []);
function even(number) {
if (number % 2 === 0) {
return true;
}
return false;
}
The push() adds elements to the end of an array and returns the new length of the array. Thus your return here is invalid.
The concat() method is used to merge arrays. Concat does not change the existing arrays, but instead returns a new array.
Better to filter, if you want a NEW array like so:
var arr = [1, 2, 3, 4];
var filtered = arr.filter(function(element, index, array) {
return (index % 2 === 0);
});
Note that assumes the array arr is complete with no gaps - all even indexed values. If you need each individual, use the element instead of index
var arr = [1, 2, 3, 4];
var filtered = arr.filter(function(element, index, array) {
return (element% 2 === 0);
});
According to the MDN document say that:
push() method: adds one or more elements to the end of an array and returns the new length of the array.
const count = ['pigs', 'goats'].push('cows');
console.log(count); // expected output: 3
concat() method is used to merge two or more arrays. This method does not change the existing arrays but instead returns a new array
console.log(['a'].concat(['b']));// expected output: Array ["a", "b"]
And combined with the final Array#reduce's parameter is the array initialize []), which means that you want to return an array result.
==> So that's the reason why in case that you use concat working well.
Refactor code
If you still want to use Array#reduce and Array#push
const even = (number) => number%2 === 0;
const result = [1, 2, 3, 4].reduce(function name(acc, curr) {
if(even(curr)) acc.push(curr); // Just add one more item instead of return
return acc;
}, []);
console.log(result);
The simpler way is to use Array#filter
const even = (number) => number%2 === 0;
console.log([1, 2, 3, 4].filter(even));
acc should not be an array. Look at the documentation. It can be one, but..
It makes no sense at all to reduce an array to an array. What you want is filter. I mean, reduce using an array as the accumulator and concating each element to it technically does work, but it is just not the right approach.
var res = [1, 2, 3, 4].filter(even);
console.log(res);
function even(number) {
return (number % 2 === 0);
}
https://dev.to/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki
Concat is 945x slower than push only because it has to create a new array.