Reputation: 1787
I posted a question a few days ago about how to flatten an array without using concat
or reduce
and I got some great answers but I still don't fully understand the solution I received. I will try and put my understanding of each line and maybe someone can point out where my reasoning is wrong.
function flatten(array) {
var l = array.length, temp;
while (l--) {
if (Array.isArray(array[l])) {
flatten(array[l]);
temp = array[l].slice();
temp.unshift(1);
temp.unshift(l);
[].splice.apply(array, temp);
}
}
}
var array = [['1', '2', '3'], ['4', '5', ['6'], ['7', '8']]];
flatten(array);
console.log(array);
Ok so first
var l = array.length, temp;
here l is just 2 and so our array length is 2 and temp
is 2
if (Array.isArray(array[l]))
if our array at position 1
(since we decremented l
by 1
in the while
loop) is indeed an array we will execute the next part of the code
flatten(array[l]);
Here we need to keep going in case we have nested arrays within an array
temp = array[l].slice();
since slice has no parameter temp
= array[1]
; so now temp = ['4', '5', ['6'], ['7', '8']]]
temp.unshift(1);
Add one to the beginning of temp. (No idea why we are doing this, and why the number one)
temp.unshift(l);
Since l
is 1
, we are putting another 1
at the beginning of the array.
Now temp = [1,1,'4', '5', ['6'], ['7', '8']]
[].splice.apply(array, temp)
; ok I seriously have NO clue what this line is doing. I have been reading documentation and I understand that apply
lets us specify a value for this
and then we can pass some parameters and a function will run ( for the example I saw), however I can't understand what the heck is the usage here. Plus splice takes out x
amount of elements from the index y
, so what are these indices? here splice
has no parameters. and whats with the empty array in the beginning []
. Please help me understand I am trying really hard!
Upvotes: 0
Views: 938
Reputation: 26706
Splice is a function which takes the index to start at, the number of elements to remove, and 0+ elements to insert inside of the hole you left.
Apply is a method that functions have (yes, functions are objects and have methods).
It takes a this
and then it takes an array of arguments to pass in.
When you have no idea how many elements you have, but you need to pass them all to a function:
// instead of
fn(a, b, c, d, e, f, g);
// you can say
fn.apply(null, args); // args being an array of arbitrary length
So you can then say:
array.splice.apply(array, args);
Where args should be the index, the number to remove, and then an arbitrary list of elements to put inside of that hole.
The []
is just a throw-away array to have quick access to splice
(to call it with apply
).
Upvotes: 0
Reputation: 351278
So your question is about this part of the code:
temp.unshift(1);
temp.unshift(l);
[].splice.apply(array, temp);
Starting with the last statement, it is calling splice
on array
, but in a specific way. Normally you would call it like this:
array.splice(l, 1, temp[0], temp[1], ...)
... which means: at index l
of array
, delete 1 element, and insert temp[0]
, temp[1]
, ... etc. The problem is that we don't know beforehand how many elements there are in temp
, but still need to provide as many arguments to splice
. And we can't just pass temp
as argument, because then array
gets an element inserted that is an array itself, which is what you just do not want to do.
While in ES6 there is an easy solution for this (see below), in ES5 you can solve this by using apply
on splice
. That apply
method accepts as first argument the object to apply the function on, and then an array which has all arguments that need to be passed to that function.
As the first two of those arguments have the meaning of index, and the number of elements to delete there (see above), and only then the remaining arguments represent which elements need to be inserted (those in temp
), there is all this shifting happening: these insert the two first arguments to pass to splice
.
Then the remaining arguments are what originally was in temp
(before the shifting). You could say that the shift
and apply
statements do the following:
array.splice(temp[0], temp[1], temp[3], temp[4], ...)
Note how temp
is playing a role for all the arguments now. And that is why the first two values were shifted in. It looks complicated, and although it works, it does not result in very readable code.
Now in ES6 it has become much simpler. You can now use the spread operator to kind of explode an array into different arguments, and functions can deal with that:
array.splice(l, 1, ...temp)
And since we don't need to shift anymore, we can safely do this without the variable temp
, and directly on array[l]
. So the complete code would look like this:
function flatten(array) {
var l = array.length;
while (l--) {
if (Array.isArray(array[l])) {
flatten(array[l]);
array.splice(l, 1, ...array[l]);
}
}
}
var array = [['1', '2', '3'], ['4', '5', ['6'], ['7', '8']]];
flatten(array);
console.log(array);
Isn't that nice and simple? ;-)
Upvotes: 1
Reputation: 26201
I would do this job like this;
var a =[['1', '2', '3'], ['4', '5', ['6'], ['7', ['8','9']]]];
Array.prototype.flat = function(){
return this.reduce((p,c) => Array.isArray(c) ? p.concat(...c).flat() : p.concat(c),[]);
};
var b = a.flat();
console.log(JSON.stringify(b));
Upvotes: 0
Reputation: 1558
I recognize the challenge from fcc .. but that's an insanely complicated way to do it. This is how is solved it.
function flatten(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
function steamroller(arr) {
return flatten(arr);
}
steamroller([1, [2], [3, [[4]]]]);
The solution you posted is insanely complicated and messy and broken, the unshifts at the end simply add 1,1 to the beginning of the array, it's a dirty way to pass the challenge ...
Upvotes: 0