I am trying to compare two given parameters of a function. The exact problem is as follows:
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
Note
You have to use the arguments object.
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3)); // expected output: [1,1]
I am using filter method to iterate over the array but I couldn't compare the args with the elements of the array inside the callback of the filter.
function destroyer(arr, ...args) {
let result = arr.filter(num => {
for (let i = 0; i<=args.length; i++ ){
num !== args[i]
}
});
return result;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I can iterate with for loop but I cannot use the output of for loop to do filter iteration.
Any ideas?
Probably an easier way to achieve the goal using .filter() with .includes(). Additionally you can use ...rest so called rest parameters for you function, see form the documentation:
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
Try as the following:
const destroyer = (arr, ...rest) => {
return arr.filter(num => !rest.includes(num));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I hope this helps!
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
Example:
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
via MDN
Filter iterates over all elements of some array and returns a new array. It puts an element in the new array only if callback (your function invoked as a parameter of filter) return true otherwise it's omitted.
Next it's worth to use rest parameters to achieve two arrays (initial and values to exclude).
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
console.log(sum(1, 2, 3));
// expected output: 6
console.log(sum(1, 2, 3, 4));
// expected output: 10
Solution with explanation:
//Declare your function, first parameter is initial array, the second one is also array created by using rest parameters
function destroyer(initialArray = [], ...toExclude) {
// filter initialArray, if el (single element) is NOT included in "toExclude" it returns true
// and add this particular element to the result array
let result = initialArray.filter(el => toExclude.includes(el) == false);
//return result
return result;
}
Related
map() can't mutate the calling array, instead it returns a new Array with modified values.
But, the following code mutating the original Array, is there any wrong in my understanding?
const arr = [1, 2, 3, 4, 5];
arr.map((num, index, arr1) => {
return arr1[index] = num * 2;
});
console.log(arr); // [2, 4, 6, 8, 10]
Well, you're mutating the original array by passing its reference into the callback function inside map() (arr1) and then manually accessing the indices. It will create a new array if you just return the value from that function.
const arr = [1, 2, 3, 4, 5];
const arr1 = arr.map((num) => {
return num * 2;
});
console.log(arr); // [1, 2, 3, 4, 5]
console.log(arr1); // [2, 4, 6, 8, 10]
The third argument to the callback function of map is the
original/source array on which the map is called upon
The arr and arr1 are both same i.e both are referencing on the same array, You can see it by using console.log(arr === arr1). So what ever you operation perform on the arr1, it gonna affect the arr.
const arr = [1, 2, 3, 4, 5];
arr.map((num, index, arr1) => {
console.log(arr1 === arr);
return num * 2;
});
You can just return num * 2 from the callback function. map internally creates a new array and return it. So you don't have to assign it as
arr1[index] = num * 2
You can also make it one-liner as:
arr.map((num, index, arr1) => num * 2)
const arr = [1, 2, 3, 4, 5];
const result = arr.map((num, index, arr1) => {
return num * 2;
});
console.log(arr); // [2, 4, 6, 8, 10]
console.log(result); // [2, 4, 6, 8, 10]
Array.map creates a new array populated with the results of calling a provided function on every element in the calling array.
Here its specifed that you must call or execute a function on every element of calling array.
What is the issue with your code?
You are not actually calling a function, you are instead updating the original array. If you are looking to create a new array by multiplying each node of the element with 2, you should do something like below.
Working Example
const arr = [1, 2, 3, 4, 5];
const newArray = arr.map((nodeFromOriginalArray, indexOfCurrentElement, arrayFromMapCalled) => {
return nodeFromOriginalArray * 2;
});
console.log(arr);
console.log(newArray);
Lets debug the paremeters inside the map function.
Here we have provided three arguments.
First argument nodeFromOriginalArray: The current element being processed in the array. This will be each node from your calling array.
Second argument indexOfCurrentElement: The index of the current element being processed in the array. Which means, the index of current element in calling array.
Third argument arrayFromMapCalled: The array map was called upon. This is the array on which the map function is getting executed. Please note, this is the original array. Updating properties inside this array results in updating your calling array. This is what happened in your case.
You should not modify your original array, which is the third parameter. Instead, you should return your node multipled by 2 inside map and assign this to a new array. Updating the third paramater inside the map function will mutate your calling array.
When calling map on an array, you provide a mapper with three arguments, an item in the array, it's index and the array itself (as you've represented in your snippet).
map takes the value returned by the function mapper as the element at the index in a new array returned by the operation.
const arr = [1,2,3,4,5]
const doubled = arr.map(x => x * 2) // [2,4,6,8, 10]
A over simplified implementation of map (without the index and originalArray params) might look like this. Let's assume that instead of being a method on the array instance, it's a function that takes an array and a mapper function.
I would not recommend re-implementing in production code, there's the native implementation as well as several libraries such as lodash and underscore that implement it.
function map(arr, mapper) {
const result = [];
for (const item of arr) {
const resultItem = mapper(item);
result.push(resultItem);
}
return result;
}
function double(x) {
return x * 2;
}
const doubled = map([1,2,3,4,5,6], double); // [2, 4, 6, 8 ,10, 12]
What I am trying to do is to return the [1, 4] array, however, I do not understand what's the mistake which ends up returning [1]. Any clues? Thank you!
const removeFromArray = function(arr) {
for (let i = arr.length - 1; i >= 0; i--) {
arr.splice(arr[i], 2);
}
return arr;
};
console.log(
removeFromArray([1, 2, 3, 4], 3, 2)
)
It's not exactly clear to me what you want to achieve.
You define a function which only takes one argument:
const removeFromArray = function(arr) {...}
But then you call the function with 3 arguments, an array and two numbers:
removeFromArray([1, 2, 3, 4], 3, 2)
Now your function only takes the first input (the array) and removes all elements instead the first one.
Please consider the syntax: splice(start, deleteCount)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Maybe this rm() does what you want?
const rm=(arr, ...rem)=>arr.filter(a=>!rem.includes(a));
console.log(rm([1, 2, 3, 4], 3, 2));
It treats the first argument as the array arr that is to be filtered. The following arguments then make up the array rem, containing all the elements that are to be taken out of array arr.
You should consider using the built in filter method for arrays.
removeFromArray = (array, unwanted, otherUnwanted) => {
const filtered = array.filter((number) => {
return number !== unwanted && number !== otherUnwanted
});
return filtered;
};
console.log(removeFromArray[1,2,3,4], 3, 2]
To make the function more scalable for future use the second parameter could be an array.
betterRemoveFromArray = (array, unwantedNumbers) => {
const filtered = array.filter((number) => {
return !unwantedNumbers.includes(number)
});
return filtered;
};
console.log(removeFromArray3([1, 2, 3, 4], [2, 3]));
You need to write a variadic function which means it accepts a variable number of arguments. To represent it, use the rest parameter syntax which allows the function to accept an indefinite number of arguments as an array.
Then, use the filter method on the 1st argument, like so:
const removeFromArray = function(arr, ...theArgs) {
arr = arr.filter(arg => !theArgs.includes(arg));
return arr;
};
console.log(
removeFromArray([1, 2, 3, 4], 3, 2)
)
In Lisps that I've seen there usually exists a 'rest' operator which returns all elements of a list in the same order, without the 0th element of that list. Without using iteration (no for or while loops), and without iteration happening via some other function or method does there exist a way to create a similar rest operator in Javascript?
For example, rest([1, 2, 3]) should return [2, 3], and rest([10, 2, 6]) should return [2, 6], and in general where 'arr' is the passed in array rest([arr[0], ..., arr[n]]) should return [arr[1], ..., arr[n]].
I had the idea of having a default value of 0 for a hidden 'counter' parameter of rest, but what I have below doesn't quite work:
function rest(arr, counter = 0)
{
if (counter === arr.length)
{
return [];
}
else
{
rest(arr, counter + 1)[0] = arr[counter]
}
}
In ES6, you can use rest syntax of Array destructuring. As from Docs:
When destructuring an array, you can unpack and assign the remaining part of it to a variable using the rest pattern:
Demo:
let rest = (arr) => {
let [first, ...rest] = arr;
return rest;
};
console.log(rest([1, 2, 3]));
console.log(rest([10, 5, 6]));
I'm trying to understand the Seek and Destroy challenge below.
Task: You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
This is the initial code below:
function destroyer(arr) {
// Remove all the values
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
After several (really) several attempts and looking at other people's code, I was able to resolve the task. However, I think it was more out of luck. I’ve copied my code below, but I was hoping someone could clarify a couple of things for me.
The code below passes whether I return val or args within the iterateThroughArray function. Why is that?
If I am supposed to be comparing ALL arguments against the first argument, where in this code am I indicating that? I kept thinking I needed to splice the first argument to compare all other arguments against it or create a variable for argument[0]. Any guidance you can provide is greatly appreciated!
function destroyer(arr) {
var args = Array.from(arguments); //this also converts them to an array
var iterateThroughArr = function (val) {
if (args.indexOf(val) ===-1){
return args;
}
};
return arr.filter(iterateThroughArr);
}
This may sound a lot to take in but here is my explanation
function destroyer(arr) {
var args = Array.from(arguments); //Here arr is converted to [Array(6),2,3]
//console.log(args)
/* var iterateThroughArr = function (val) {
if (args.indexOf(val) ===-1){
return args;
}
};
return arr.filter(iterateThroughArr);
*/
// to make more clear the above code can be rewritten as below
var arr = arr.filter(function (val) {
console.log("args = "+ args + " val = " + val + " indexOf(val) " + args.indexOf(val) )
// here you are itterating through each arr val which in this case is[1,2,3,1,2,3]
// if you dont believe me uncomment the next console.log() and see the output
// console.log(val)
if (args.indexOf(val) ===-1){
// here is where the magic happens
// Now you are checking if val exisists by using a builtin method called .indexOf()
// In general, .indexOf() returns -1 if a value does not exist within an array
//Below is working example
/* var array = [1,2,3,4,5]
console.log(array.indexOf(1)) // 0 since it is located at index 0
console.log(array.indexOf(5)) // 4 since it is located at index 4
console.log(array.indexOf(10)) // -1 since it does not exisit
*/
// Therefore, if value passes the above if statement
//then that means it doesnot exisit on args([Array(6),2,3])
//which as a result will be included on the filtered array
return args;
}
});
return arr;
}
var val = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
//console.log(val)
Basically, what you need to understand is how filter works and how .indexOf works.
For more detail visit Mozilla documentation: .indexOf() and .filter()
Give this a try:
function destroyer(arr) {
// arr1 is equal to the array inside arr
var arr1 = arr.slice(arguments);
// arr2 becomes an array with the arguments 2 & 3
var arr2 = Array.prototype.slice.call(arguments, 1);
// this function compares the two and returns an array with elements not equal to the arguments
return arr1.concat(arr2).filter(function (item) {
return !arr1.includes(item) || !arr2.includes(item)
})
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I'm new to JS and I have a task to flatten an array. What I have is [1,2,[3,4]] and I have to turn it into [1,2,3,4]. I use the following approach
function flatten(arr){
return arr.reduce(function(a, b) {
return a.concat(b);
});
}
But it says concat is not a function.
Array.isArray(arr); //returns true
Can someone tell me what to do here :)
Your arr value is [1,2,[3,4]]. That's an array with three entries: the number 1, the number 2, and the array [3,4].
The first two arguments to a reduce callback are
the accumulator (which is the return value of the previous invocation of the callback) and
a value taken in sequence from the array.
You don't supply an initial accumulator in your code, so that means the first call to the reduce callback accepts the first two entries as arguments (i.e., the first element of the array is used as an accumulator). concat is not a function because the initial value of a is a number, not an array. You tested to see if arr was an array, but did not test to see if a was an array.
The clear solution here is to supply an initial value to use as an accumulator:
function flatten(arr){
return arr.reduce(function(a, b) {
return a.concat(b);
}, []);
}
This creates an empty array (with []) and then supplies it as the a value for the first call to the reduce callback.
You can use this solution:
var array = [].concat.apply([], [1,2,[3,4]]);
console.log(array);
DOCS about the array says
The JavaScript Array object is a global object that is used in the construction of arrays; which are high-level, list-like objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
In your example for reduce you will have to provide an initial value which can b an empty array.
var flattened = [1,2,[3,4]].reduce(function(a, b) {
return a.concat(b);
}, []);
console.log(flattened)
You just need to provide array [] after the callback function as initial value to reduce, otherwise that value will be first element of your array. So in your code in first iteration of reduce you are trying to do this a.concat(b) where a is 1 and b is 2 and that throws the error.
function flatten(arr) {
return arr.reduce(function(a, b) {
return a.concat(b);
}, []);
}
console.log(flatten([1, 2, [3, 4]]))
concat is expecting an Array of values to join with another Array. The reason this is failing is because your Array isn't a full matrix.
The problem with your array is that it wouldn't be wise to create a single hard coded solution for that specific Array, you should probably create a recursive function like:
function flattenArr(arr) {
const newArr = []
function recursiveFlat(a) {
if(!Array.isArray(a)) {
newArr.push(a);
}
else {
for (let i = 0; i < a.length; i++) {
if(Array.isArray(a[i])) {
recursiveFlat(a[i]);
}
else {
newArr.push(a[i]);
}
}
}
}
arr.forEach(item => recursiveFlat(item));
return newArr;
}
arr = [1, 2, [3, 4]]
console.log(flattenArr(arr))
That way it will always flatten the array no matter the shape.
You just make sure that arr is an array. But,you call concat on a.
test this:
if(a instanceof Array)
You can use Array.prototype.concat() combined with Spread syntax:
var flattened = [].concat.call([], ...[1, 2, [3, 4]]);
console.log(flattened);