Can anyone give me a hint if this is going in the right direction? This code works, except it doesn’t account for nested arrays. Which I guess is the point of the challenge. I stumbled onto a solution using recursiveness while searching, but I’d like to see if there’s any way to salvage the code I have here. Any hints?
function steamrollArray(arr) {
var myArray = [];
var flatten = function(arr) {
for (var i=0; i<arr.length; i++) {
if (Array.isArray(arr[i]) === true) {
for(b=0; b<arr[i].length; b++) {
myArray.push(arr[i][b]);
}
}
else if (Array.isArray(arr[i]) === false){
myArray.push(arr[i]);
console.log(arr[i]+ " is not an array");
}
}
}
flatten(arr);
document.write(myArray);
return myArray;
}
I don’t want to give too much away, but I think the easiest way to handle this is recursion, basically having your function call itself.
Look at it this way, your function is designed to take an array of elements and return an array of elements. As you cycle through the elements, if you encounter a subarray, you call your function again with that subarray, which your function will return with a flattened array that you can concat onto your master array. It twists your brain a little, but if designed right, your function will just keep calling itself until it runs out of subarrays and then will unravel itself, returning all of the array elements.
Research recursion. It’s a trippy solution that helps solve some weird problems. Don’t get frustrated with it - it really can do some cool things.
2 Likes
Thanks, I’ll look at recursion again. You’re right, it does twist my brain a little, but your explanation helps, especially the part about the function calling itself if it encounters a subarray. This was encouraging, I’ll keep plugging away to get my own solution using recursion.
Your code follows the same logic pattern that mine does:
- Setup your empty results array
- Iterate through the array and check if that element is an array or not, if it is not then add it to results, else continue looking.
- …repeat
- return results
The thing is that you can’t know how many level you might need to dig into the array to get the value, and for this reason you need a way to do so indefinitely like a while loop or recursion.
1 Like
Okay, I got this code to pass. There’s a much more elegant solution here:
https://codetuts.tech/flatten-deep-nested-array-object/#othersolutions
But I wanted to try my own solution instead of copying and pasting, and I can’t claim to truly understand that solution (the ternary operator nested in the concat function is a mystery to me).
So, I tried to come up with something more verbose, but understandable to me. I went ahead and tried to stick with my original code since RadDog25 was hinting the basic structure was sound. I just added a line to the first if statement that goes back and calls the function again, but this time passing the subarray, as ksjazzguitar suggested. That seemed to do the trick. Thanks so much for the help!
function steamrollArray(arr) {
var myArray = []; // create empty array to hold flattened array elements
var flatten = function(arr) {
for (var i=0; i<arr.length; i++) { // iterate through inital array passed to steamrollArray function
if (Array.isArray(arr[i]) === true) { // check if element is an array
var currentArray = arr[i]; // if element is array, assign value to variable,
flatten(currentArray); // then go back a step and call the function again with that variable
}
else if (Array.isArray(arr[i]) === false) { // if element isn't an array, push onto myArray
myArray.push(arr[i]);
}
}
}
flatten(arr);
return myArray;
}
Can be written as :
if (Array.isArray(arr[i]))
isArray() returns (true || false), so no need to compare.
Less clear code is easier to manage (or so they say).
1 Like
Cool, good job.
You’re on your way to learning recursion. It’s a weird thing, but once you start to get it, it will make more sense. It’s hard to wrap your head around. I’d look for tutorials on youtube - a visual representation may help. It’s a technique with limited application, but when you need it, it’s awsome.
I agree with MyTrueName, I would add that you don’t need the else after it (which would have been more standardly written as “(!Array.isArray(arr[i]))”. But you don’t need it anyway - you’ve already tested if it’s an array. Anything that reaches that else will already be confirmed to not be an array. I think it should be:
if (Array.isArray(arr[i]) === true) {
var currentArray = arr[i];
flatten(currentArray);
} else {
myArray.push(arr[i]);
}
And yes, ternary operators look weird. But they’re pretty easy to learn.
1 Like
Oops, also get rid of the variable currentArray - it serves no purpose. I assume it’s left over from some other line of thinking. Just push arr[i]:
if (Array.isArray(arr[i]) === true) {
flatten(arr[i]);
} else {
myArray.push(arr[i]);
}
But of course, when you have only one line in each of the if/else statements, they’re practically begging to be a ternary operator.
Array.isArray(arr[i]) ? flatten(arr[i]) : myArray.push(arr[i]);
It just evaluates the first statement. If it’s true, it does the second statement. If not, it does the third statement. It looks weird, but you get used to it.
1 Like
Thanks, @ksjazz guitar. Per your suggestion, I got rid of currentArray (I guess I put it in there as an extra baby step to help me understand) and got rid of the else statement as well (thanks @RadDog25). Your explanation of the ternary operator helps clear up what was confusing me in the example I found on the internet, which was tricky to understand because in this case the ternary operator was being passed to the concat method:
function flatten(arr) {
return arr.reduce(function(explored, toExplore) {
return explored.concat(
Array.isArray(toExplore)?
flatten(toExplore) : toExplore
);
}, []);
}
But now I think I see that if Array.isArray() evaluates to true nothing will actually be passed to the concat method, it just steps backward and executes the function again and keeps looking, using reduce until it burrows inside the passed array to the bottom level containing array elements. At which point isArray() will evaluate to false and it can be tacked onto the array to be returned. Or something like that. 
I know recursion is confusing. If it is an array, it will just call flatten to dig deeper. Eventually it will find an element that is not an array (it’s not an infinite nest of arrays), and then that will get pushed. Then it will back out and unravel itself, pushing all non-array elements as it goes. I know it’s weird. It just digs through the array - anything that’s an array causes it to dig deeper, anything that isn’t gets pushed.
Like I said, look at some videos on recursion. Don’t worry, it’s normal to be confused. Watch some videos and do some examples and then it will click. And then you’ll have a very powerful tool at your disposal.
1 Like
You can check out this recursive solution I wrote, it might help.
function steamrollArray(arr) {
if(arr && arr.constructor === Array) {
return arr.reduce(function(acc, x) {
return acc.concat(x && x.constructor === Array ? steamrollArray(x) : x);
}, []);
}
}
steamrollArray([1, [2], [3, [[4]]]]);