So I have my function uniteUnique. It should get the arguments and concatonate them to a single array. If there would be a specific amount of arguments for example 3, i would implement it like the function bellow
function uniteUnique(arr) {
var args = [];
var newArgs;
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
newArgs = args[0].concat(args[1], args[2]);
}
return newArgs ;
}
uniteUnique([1, 3, 2], [1, [5]], [2, [4]]);
But what if uniteUnique function would have 2 arguments or any other number.
uniteUnique([1, 2, 3], [5, 2, 1])
How to make my function know how many arguments it should concatonate, and how would it be implemented inside the concat() function ?
EDIT:
Output should look like this:
uniteUnique([1, 3, 2], [1, [5]], [2, [4]]) should return [1,3,1,1,[5],2,[4]]and uniteUnique([1, 2, 3], [5, 2, 1]) should return [1,2,3,5,2,1]
I'm not sure if I fully understand your question, but: what about this simple solution?
function uniteUnique() {
return Array.prototype.slice.call(arguments);
}
The arguments object is not really an Array instance, and does not have any of the Array methods. So, arguments.slice(...) will not work because the arguments object does not have the slice method.
Instead, Arrays do have this method, and because the arguments object is very similar (...) to an array, the two are compatible. This means that we can use array methods with the arguments object. And array methods will return arrays rather than other argument objects.
For a more throughtful explanation please see this SO answer ...
UPDATE (to answer OP comment):
If you need deep merging, you can do:
function uniteUnique() {
return Array.prototype.concat.apply([], arrays);
}
or even:
function uniteUnique() {
return [].concat.apply([], arrays);
}
This should work since the dafaule value of Symbol.isConcatSpreadable is false, so concat() acts deeply...
According to your examples you want to flatten arguments array. In ES6 you can use Rest parameters to get arguments array and Spread syntax to flatten it:
function uniteUnique(...args) {
return [].concat(...args);
}
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments
You can access the arguments variable to loop through all arguments passed to a given function.
Was able to solve this like that,
function uniteUnique(arr) {
var myNewArray = [].concat.apply([], Array.prototype.slice.call(arguments));
return myNewArray;
}
uniteUnique([1, 3, 2], [1, [5]], [2, [4]]) ;
Related
Im trying to return the values in argument[0] that are not equal to the values of arguments[1] and so on.
Ive created a variable 'let argArr' that holds the values of the arguments after the first, Im trying to understand why in my .filter() i cannot target 'let argArr'?
function destroyer(arr) {
let argArr = [];
for (let i = 1; i < arguments.length; i++) {
argArr.push(arguments[i])
}
return arr.filter(i => i !== argArr)
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
You are comparing an arr array's element to the argArr what is wrong.
The right way is to check if element is inside argArr and filter if yes.
return arr.filter(i => !argArr.includes(i))
Instead of using arguments explicitly specify the array as the first argument, and then use rest parameters to gather all the remaining arguments into an array. Then use filter to return only those elements in the array that are not in the rest array.
function destroyer(arr, ...rest) {
return arr.filter(el => !rest.includes(el));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
Use spread operator to treat arguments as normal array
function foo() {
return [...arguments].filter(el => el);
}
console.log(foo(1,2,3))
I saw this function , though it works fine but I am bit puzzled about the function expressions. Here is the code
mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(fn(arr[i]))
}
return newArr;
}
can anybody explain to nme what this rather complicated code is actually doing?
Lets say you have var array = [1, 2, 3, 5]; and then run var array2 = mapForEach(array, function(i) { return i * 2; })
array2 would then contain [2, 4, 6, 10].
So it returns a new array where you have the ability to modify each record with a function
mapForEach enumerates an array and calls a supplied function on each element.
example:
var a = [1, 2, 3];
console.log(mapForEach(a, (x) => x * 2));
would create a new array with the values (and output to console):
[2, 4, 6]
Basically it is an implementation of javascript native array function map, which creates a new array with the results of calling a provided function on every element in this array.
More info about mentioned function you can find here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
I've been trying to understand this function for quite a while, but it just doesn't make sense to me. The goal of the function is to remove any numbers within the array of arguments that match the other argument numbers.
Why is it necessary to slice the array for the function to work?
Is args.splice(0,1) redundant? I removed it and nothing changed.
It seems like the filter function does the bulk of the work, but I don't see how it actually filters for the numbers...
function destroyer(arr) {
var args = Array.prototype.slice.call(arguments);
args.splice(0, 1);
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Let's go through it line by line:
var args = Array.prototype.slice.call(arguments);
JavaScript's arguments variable is similar to an array but it's not an array. You can try this yourself: arguments instanceof Array will give false. So applying the slice method from the Array prototype will simply convert arguments to a real array.
args.splice(0, 1);
This is to remove the first argument, which is arr in your case.
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
This will go through all the numbers in arr and will check each one of them if it exists in the arguments. When indexOf() returns -1 it means the element was not found in the array.
Slice does not alter. It returns a shallow copy of elements from the original array. Elements of the original array are copied into the returned array.
Take this example
var object = {
'0': 'zero',
'1': 'one',
'2': 'two',
'3': 'three',
'4': 'four',
length: 5
};
var sliced = Array.prototype.slice.call( object, 3 );
['three','four']; //output
passe Arguments [Array[6], 2, 3]
Arguments after splicing or remove first element of argument [2, 3]
so closure function filters element which is present in first element of array with other two element. returning just [1, 1]
To understand what's going on, we need to understand the Function.prototype.call method.
It invokes the Array.prototype.slice method on the first argument you pass to it, which in this case is the magical JS arguments object, and then passes in whatever arguments follow.
Thus Array.prototype.splice is unnecessary, and you can just write:
function destroyer(arr) {
var rest = Array.prototype.slice.call(arguments, 1);
return arr.filter(function(element) {
return rest.indexOf(element) === -1;
});
}
in fact, this has been implemented in ES2015+ with the spread operator, so you could write:
function destroyer(arr, ...rest) {
return arr.filter(function(element) {
return rest.indexOf(element) === -1;
});
}
I've added comments, please see if it helps you to understand the function.
function destroyer(arr) {
// arr just holds [1, 2, 3, 1, 2, 3]
var args = Array.prototype.slice.call(arguments);
// args contains nested array with all input params [[1, 2, 3, 1, 2, 3], 2, 3]
args.splice(0, 1);
//args is spliced and we have [2,3] in args
//Filter arr=[1, 2, 3, 1, 2, 3] elements, condition it must not be in args i.e [2,3]
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
Please refer to the below documentation to read about arguments object used in this function:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments
In arr we will have [1,2,3,1,2,3] and
in args we will have [[1,2,3,1,2,3],2,3]
The filter function loop over arr
and args.indexOf(element) will return -1 if element is not in args.
So, for the first time in loop the element value is 1 and inside loop
args.indexOf(1) returns -1 because 1 is not present in args because at 0 index we have array and at 1st index we have 2 and at 2nd index we have 3. So the condition === -1 is true and returns 1 to the array that is going to be printed to the console.
for next element, i.e., 2 in arr, the statement args.indexOf(2) returns the first index at which 2 is present i.e., 1 in args array. for likewise entire loop will be executed for arr
I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)
All you know that arguments is a special object that holds all the arguments passed to the function.
And as long as it is not an array - you cannot use something like arguments.slice(1).
So the question - how to slice everything but first element from arguments?
UPD:
seems like there is no way without converting it to an array with
var args = Array.prototype.slice.call(arguments);
If someone posts another solution it would be great, if not - I'll check the first one with the line above as an answer.
Q. How to slice everything but first element from arguments?
The following will return an array containing all arguments except the first:
var slicedArgs = Array.prototype.slice.call(arguments, 1);
You don't have to convert arguments to an array first, do it all in one step.
Meddling with array functions is not actually necessary.
Using rest parameter syntax ...rest is cleaner and more convenient.
Example
function argumentTest(first, ...rest) {
console.log("First arg:" + first);
// loop through the rest of the parameters
for (let arg of rest) {
console.log("- " + arg);
}
}
// call your function with any number of arguments
argumentTest("first arg", "#2", "more arguments", "this is not an argument but a contradiction");
...Rest
See the example Fiddle
See MDN Documentation page
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments:
You should not slice on arguments because it prevents optimizations in
JavaScript engines (V8 for example). Instead, try constructing a new
array by iterating through the arguments object.
So Paul Rosiana's answer above is correct
This can be a way:
var args = Array.from(arguments).slice(1);
You can "slice without slicing" by procedurally walking the arguments object:
function fun() {
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
return args;
}
fun(1, 2, 3, 4, 5); //=> [2, 3, 4, 5]
You can use the method [].slice.call(arguments, 1)
[].slice will return you the slice function object and you can call it as the arguments and 1 are the parameters
You can use ...rest within the function to separate the first and the rest of the arguments:
function foo(arr) {
const [first, ...rest] = arguments;
console.log(`first = ${first}`);
console.log(`rest = ${rest}`);
}
//Then calling the function with 3 arguments:
foo(1,2,3)
you can use this too
function arg(myArr) {
let arg = Object.values(arguments).slice(2, 4);
console.log(arg);
return arg;
};
arg([1, 2, 3], 4, [5,6], 7)
see here for reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values
...
Arguments type is iterable, so using the ES6 (ES2015) spread ... operator, then use Array.slice([start], [end]) method, such as
function omitFirstAndLastArgument(value) {
const args = arguments.length > 2 ? [...arguments].slice(1, -1) : [];
return args;
}
omitFirstAndLastArgument(1, 2, 3, 4, 5, 6); // [2, 3, 4, 5]