I was doing some javascript exercises online (codewars.com). One problem asked the user to take an array of array objects and remove one level from the entirety of the array.
[] /* becomes */ []
[[1, 2, 3], ["a", "b", "c"], [1, 2, 3]] /* becomes */ [1, 2, 3, "a", "b", "c", 1, 2, 3]
[[3, 4, 5], [[9, 9, 9]], ["a,b,c"]] /* becomes */ [3, 4, 5, [9, 9, 9], "a,b,c"]
I ended up learning about the concat method, but the most popular solution used this statement...
function (arr){
return [].concat.apply([],arr);
}
Can someone please explain the usage of [] here? I can't understand how this produces the correct results (and it doesn't give explanation on the page). I know that there are plenty of other times in which empty brackets are used as parameters and labeling arrays so understanding the usage here may help me be able to use it myself in the future.
Lets split this across multiple lines so it is easier to describe. The description of line D specifically is what answers your question
[] // A
.concat // B
.apply( // C
[], // D
arr // E
);
A Is an Array, but here it is just being using as a shortcut to Array.prototype so we can access..
B The concat method from Array.prototype
C which we then invoke (using apply) with a
D this argument of a new Array, to be the base Object and
E a list of arguments, which was our previous arr
So, you could re-write this using Array.prototype and call as
var new_base_arr = [];
Array.prototype.concat.call(
new_base_arr,
arr[0],
arr[1],
...
arr[n]
);
Which might look more familiar to you written as
new_base_arr.concat(arr[0], arr[1], ..., arr[n]);
The problem being solved here is invoking a function with an undetermined number of arguments.
Related
Guys this might be a simple question, but please help.
i have a data.
var a = { "data":[[1,2,3],[2,4,3],[3,6,7],[1,4],[6,4,3,4],[6,7,3,5]] }
i'm plotting a multiple line chart using chartjs and i want these valus in array to use as datasets.
what i wat is to save each array in different var's
like;
var a = [1,2,3],
var b = [2,4,3]
var c = [3,6,7]
so that i can pass theese values to chart js and plot chart. any help is appreciated. i thought of foreach and getting by each position. but its not working.
regards
Use Array destructuring.
Use spread operator on last node. That will keep all the remaining nodes except the specified number of paramaters, if you are interested on the frst three nodes only.
var data = { "data": [[1, 2, 3], [2, 4, 3], [3, 6, 7], [1, 4], [6, 4, 3, 4], [6, 7, 3, 5]] }
const [a, b, c, ...restNodes] = data.data;
console.log(a);
console.log(b);
console.log(c);
console.log(restNodes);
Please Note Its not mandatory to have the last node with spread operator. You can pick the first three nodes only using
const [a, b, c] = data.data;
I just said you can do this aswell
Spread the data and just assign to three variables.
var x = { "data":[[1,2,3],[2,4,3],[3,6,7],[1,4],[6,4,3,4],[6,7,3,5]] }
let [a,b,c] = [...x.data];
console.log(a);
console.log(b);
console.log(c);
There is no need to even include a fourth variable if all you care about is a,b,c.
I came across this JS problem but I can't figure out the syntax of how it's working, could someone please help to explain? I don't understand the empty square bracket syntax at the start and then how the concat is being applied with another empty square bracket? it's just quite confusing for me.
Appreciate any help to step through this.
let arr = [[1, 2],[3, 4],[5, 6, 7, 8, 9],[10, 11, 12]];
let flattened = [].concat.apply([], arr);
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
let arr = [[1, 2],[3, 4],[5, 6, 7, 8, 9],[10, 11, 12]];
let flattened = [].concat.apply([], arr);
*first array*---| |---*second array*
The first array has one goal:
to invoke concat() method with this. Here this is the second array. Then the first array is thrown away
Then apply() method flattens the arr array to one level.
You can check it:
let foo = [0].concat([1,2,3], [[1,2,3]] );
console.log(foo)
UPDATE:
In addition, the first array can be removed:
const arr = [[1, 2],[3, 4],[5, 6, 7, 8, 9],[10, 11, 12]];
const flattened = Array.prototype.concat.apply([], arr);
console.log(flattened)
Some info how apply() works.
In addition, it would be really useful to know how apply() flattens array items to one level or how apply() takes an array of arguments and treats each element of that array as a single argument.
Let me show a simple example. We have the following object and method:
const showFullName = (arg_1, arg_2) => {
console.log(`arg_1 is ${arg_1}`);
console.log(`arg_2 is ${arg_2}`);
}
let foo = {};
showFullName.apply(foo, ['firstName', 'surname']);
The [] is an empty array. The concat function concatenate two or more arrays: [].concat(arr[0], arr[1], arr[2], arr[3])
The catch here is to know how apply works. The second parameter of apply is an array of parameters. It's like doing this (if you're familiar with the concept of desctructuring):
[].concat(...arr)
You can read more about apply here
I have been doing this course for hours on free code camp, however, I found a solution that I do not understand and I am trying to put comments on each line to record as I achieve and understand it for future references and I already understand some lines but I cannot understand some parts of this code:
function destroyer(arr) {
// let's make the arguments part of the array
var args = Array.prototype.slice.call(arguments); // this would result into [[1, 2, 3, 1, 2, 3], 2, 3]
args.splice(0,1); // now we remove the first argument index on the array so we have 2,3 in this example
// I DO NOT UNDERSTAND THESE CODES BELOW
return arr.filter(function(element) {
return args.indexOf(element) === -1;
});
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
I already check on documentation and I find it hard to understand seems the code in this sample are very different. I would really appreciate your help!
arr in the section of code you don't understand refers to the first argument passed to the destroyer function; in this case, the array [1, 2, 3, 1, 2, 3]
arr.filter is using the Array.filter method to create a "filtered" version of the array with only those values that pass the "test" defined by function(element) { return args.indexOf(element) === -1; }
That function uses Array.indexOf to check if the sliced args array (which you correctly identified as being equal to [2, 3]) contains the given element. Because indexOf returns -1 when the element is not found, checking for that value is equivalent to checking that the specified element is NOT in the array
The result of all of this - and the return value of the function destroy - will be the array [1, 1], representing a filtered version of the array passed to destroy that contains all the values not equal to the other values passed to destroy.
Array.slice is part of the arrays prototype;
prototype methods are only accessable on instances of Classes.
var arr = ['a', 'b', 'c', 'd'];
// [] is JavaScript shorthand for instantiating an Array object.
// this means that you can call:
arr.slice(someArg1);
arry.splice(someArg2);
My question is simple. I started with Ramda recently, and I like it, for it is pure functional. I have a little issue with concat function, as it only accepts two lists as arguments. So, if I need to concat three lists or more, I have to chain functions like this: concat(list1, concat(list2, concat(list3, list4))) (for a four lists concatenation). Is there any better way to do this, that I don't know? Thanks.
If you want to concatenate a list of lists, you can reduce the list using R.concat with an empty list as the initial value.
const concatAll = R.reduce(R.concat, []);
concatAll([[1, 2], [3, 4], [5, 6]]);
Or just pass the lists directly to R.reduce.
R.reduce(R.concat, [], [[1, 2], [3, 4], [5, 6]]);
If you want to create a new variadic function that takes each list as a separate argument, then you can wrap the function with R.unapply.
const concatAll_ = R.unapply(R.reduce(R.concat, []));
concatAll_([1, 2], [3, 4], [5, 6]);
Ramda also has an R.unnest function, which when given a list of lists will concatenate them together.
R.unnest([[1, 2], [3, 4], [5, 6]]);
I haven't used the Ramda library, but you appears to be using this in node.js from the documentation I've read in the link you posted. In that case, you can use the arguments variable in a function in node.js to write your own concat function that takes n lists as input. The arguments variable is essentially an array of the arguments inputted into the function.
function myConcat () {
for (var i = 1; i < arguments.length; i++) {
concat(arguments[0], arguments[i]);
}
return arguments[0];
};
In this case however, you would probably have to call it like:
list1 = myConcat(list1, list2, list3, list4);
Depending on exactly what you are concatenating and your environment (ES2015 required), you could do:
const newList = [...list1, ...list2, ...list3];
Otherwise, you are stuck with multiple calls to concat, though you could make it a little cleaner with compose:
const newList = compose(concat(list1), concat(list2), concat(list4))(list3);
really though you want to map or better, reduce:
const newList = reduce((acc, x) => concat(acc, x), [list3], [list4, list2, list1]);
or that inner reduce function could look like:
(acc, x) => [...acc, ...x]
Using R.unnest with R.unapply allows you to call your function as a variadic:
const unnestAll = R.unapply(R.unnest)
unnestAll([1, 2], [3, 4], [5, 6, 7]) //=> [1, 2, 3, 4, 5, 6, 7]
A slight variation on Scott Cristopher's answer
See Example in repl
You can use flatten method this way:
R.flatten([list1, list2, list3, list4, ])
Technically you pass a single array, which contains all your lists. Then you just flatten them all into a single array.
Do NOT use this method if some of your lists might contain list themselves - they'll be flattened as well.
I want to replace elements in some array from 0 element, with elements of another array with variable length. Like:
var arr = new Array(10), anotherArr = [1, 2, 3], result;
result = anotherArr.concat(arr);
result.splice(10, anotherArr.length);
Is there some better way?
You can use the splice method to replace part of an array with items from another array, but you have to call it in a special way as it expects the items as parameters, not the array.
The splice method expects parameters like (0, anotherArr.Length, 1, 2, 3), so you need to create an array with the parameters and use the apply method to call the splice method with the parameters:
Array.prototype.splice.apply(arr, [0, anotherArr.length].concat(anotherArr));
Example:
var arr = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];
var anotherArr = [ 1, 2, 3 ];
Array.prototype.splice.apply(arr, [0, anotherArr.length].concat(anotherArr));
console.log(arr);
Output:
[ 1, 2, 3, 'd', 'e', 'f', 'g', 'h', 'i', 'j']
Demo: http://jsfiddle.net/Guffa/bB7Ey/
In ES6 with a single operation, you can do this to replace the first b.length elements of a with elements of b:
let a = [1, 2, 3, 4, 5]
let b = [10, 20, 30]
a.splice(0, b.length, ...b)
console.log(a) // -> [10, 20, 30, 4, 5]
It could be also useful to replace the entire content of an array, using a.length (or Infinity) in the splice length:
let a = [1, 2, 3, 4, 5]
let b = [10, 20, 30]
a.splice(0, a.length, ...b)
// or
// a.splice(0, Infinity, ...b)
console.log(a) // -> [10, 20, 30], which is the content of b
The a array's content will be entirely replaced by b content.
Note 1: in my opinion the array mutation should only be used in performance-critical applications, such as high FPS animations, to avoid creating new arrays. Normally I would create a new array maintaining immutability.
Note 2: if b is a very large array, this method is discouraged, because ...b is being spread in the arguments of splice, and there's a limit on the number of parameters a JS function can accept. In that case I encourage to use another method (or create a new array, if possible!).
In ES6, TypeScript, Babel or similar you can just do:
arr1.length = 0; // Clear your array
arr1.push(...arr2); // Push the second array using the spread opperator
Simple.
For anyone looking for a way to replace the entire contents of one array with entire contents of another array while preserving the original array:
Array.prototype.replaceContents = function (array2) {
//make a clone of the 2nd array to avoid any referential weirdness
var newContent = array2.slice(0);
//empty the array
this.length = 0;
//push in the 2nd array
this.push.apply(this, newContent);
};
The prototype function takes an array as a parameter which will serve as the new array content, clones it to avoid any weird referential stuff, empties the original array, and then pushes in the passed in array as the content. This preserves the original array and any references.
Now you can simply do this:
var arr1 = [1, 2, 3];
var arr2 = [3, 4, 5];
arr1.replaceContents(arr2);
I know this is not strictly what the initial question was asking, but this question comes up first when you search in google, and I figured someone else may find this helpful as it was the answer I needed.
You can just use splice, can add new elements while removing old ones:
var arr = new Array(10), anotherArr = [1, 2, 3];
arr.splice.apply(arr, [0, anotherArr.length].concat(anotherArr))
If you don't want to modify the arr array, you can use slice that returns a shallow copy of the array:
var arr = new Array(10), anotherArr = [1, 2, 3], result = arr.slice(0);
result.splice.apply(result, [0, anotherArr.length].concat(anotherArr));
Alternatively, you can use slice to cut off the first elements and adding the anotherArr on top:
result = anotherArr.concat(arr.slice(anotherArr.length));
I'm not sure if it's a "better" way, but at least it allows you to choose the starting index (whereas your solution only works starting at index 0). Here's a fiddle.
// Clone the original array
var result = arr.slice(0);
// If original array is no longer needed, you can do with:
// var result = arr;
// Remove (anotherArr.length) elements starting from index 0
// and insert the elements from anotherArr into it
Array.prototype.splice.apply(result, [0, anotherArr.length].concat(anotherArr));
(Damnit, so many ninjas. :-P)
You can just set the length of the array in this case. For more complex cases see #Guffa's answer.
var a = [1,2,3];
a.length = 10;
a; // [1, 2, 3, undefined x 7]