John Behan
John Behan

Reputation: 584

Recursion issue using Javascript Issue

I am working on this challenge from FreeCodeCamp

Flatten a nested array. You must account for varying levels of nesting.

I am trying to solve it using recursion.

Here is my code:

function steamroller(arr) {
    var flatArray = [];

    function flatten(obj) {
        if (!Array.isArray(obj)) {
            return(obj);
        }
        for (var i in obj) {
            return flatten(obj[i]);
        }
    }

    flatArray.push(flatten(arr));

    console.log(flatArray);
}

steamroller([1, [2], [3, [[4]]]]);

This logs:

Array [ 1 ]

I can see the problem, the return statement breaks the for loop so only the first value is returned.

However if I leave out the return and just use:

flatten(obj[i]);

I get back:

Array [ undefined ]

What should I do to fix this?

Upvotes: 0

Views: 280

Answers (6)

Scott Sauyet
Scott Sauyet

Reputation: 50787

Because this was recently brought back up with a new answer, perhaps it's time to revisit it.

This is a fairly simple recursive process. When this question was asked, ES5 was ubiquitous and ES6/ES2015 was being implemented everywhere.

A simple ES5 solution would look like this:

const flat = (ns) => 
  ns .map (n => Array .isArray (n) ? flat (n) : n)
     .reduce ((a, b) => a .concat (b), [])

but this pattern was made unnecessary by ES6, and that map-reduce(concat) pattern could now be written with Array.prototype.flatMap:

const flatter = (ns) => 
  ns .flatMap(n => Array .isArray (n) ? flatter (n) : n)

However, you don't even need to do this, because ES6 also introduced Array.prototype.flat, which allows us to write this:

const flattest = (ns) =>
  ns .flat (Infinity)

const flat = (ns) => 
  ns .map (n => Array .isArray (n) ? flat (n) : n)
     .reduce ((a, b) => a .concat (b), [])

const flatter = (ns) => 
  ns .flatMap(n => Array .isArray (n) ? flatter (n) : n)

const flattest = (ns) =>
  ns .flat (Infinity)


const data = [1, [2], [3, [[4]]]]

console .log (flat (data))
console .log (flatter (data))
console .log (flattest (data))

Upvotes: 0

Luffy Liu
Luffy Liu

Reputation: 1

function steamrollArray(arr) {

  let string = String(arr);
  let strArr = string.split(",");
      
  for (let i in strArr){

    if (strArr[i]==""){
      strArr.splice(i,1); 
      
    }

    if (strArr[i]=="[object Object]"){
      strArr[i] = {}; 
    }

    if (isNaN(parseInt(strArr[i],10))== false){
      strArr[i] = parseInt(strArr[i]); 
    }

  }

  return strArr;

}

Upvotes: 0

ThiagoSckianta
ThiagoSckianta

Reputation: 156

A fully recursive solution, if you don't need to keep the original array intact:

function flatten (obj, memo) {
    memo = (memo || []);
    if (Array.isArray(obj)) {
        if (obj.length) {
            // flatten the first element, removing it from the original array
            flatten(obj.shift(), memo);
            // flatten the rest of the original array
            flatten(obj, memo);
        }
        return memo;
    }
    return memo.push(obj);
}

We firstly check if obj is an array. Then, if it is not empty, flatten the first element, them flatten the rest. If object is not an array, it is returned and pushed into the memo array, wich is returned.

Notice that in the first line I'm setting memo = (memo || []). This makes sure we always get a new array in the first call, if memo is not set.

Upvotes: 1

Carsten Massmann
Carsten Massmann

Reputation: 28196

Here is a slightly different approach:

function flat(a,f){
 if (!f) var f=[];
 a.forEach(function(e){
  if (Array.isArray(e)) flat(e,f);
  else f.push(e);
 });
 return f;
}

var flatArray=flat(deepArray);

The function returns the flattened array each time but ignores its return value in the inner calls of itself (flat(e,f)). Instead it keeps pushing each non-Array value to the same f-Array that was defined initially in the outer call of flat(a).

Using the second (usually unused) argument you can also concatenate values to an already existing flat array like

var a=[[2,4,[5,6,[7,8],[9,10]],1],3];
var fl1=flat(a);
// "2|4|5|6|7|8|9|10|1|3"
var fl2=flat(a,fl1);
// "2|4|5|6|7|8|9|10|1|3|2|4|5|6|7|8|9|10|1|3"

Upvotes: 1

Nina Satragno
Nina Satragno

Reputation: 601

I modified your code so that it works, adding comments:

function steamroller(arr) {
var flatArray = [];

function flatten(obj) {
    if (!Array.isArray(obj)) {
        // We got to the innermost element. Push it to the array.
        flatArray.push(obj); 
        return;
    }
    for (i = 0; i < obj.length; i++) {
        flatten(obj[i]); // Do not return here.
    }
}

flatten(arr);

console.log(flatArray);
}

steamroller([1, [2], [3, [[4]]]]);

Upvotes: 1

CD..
CD..

Reputation: 74096

You need to add the items to the result array.

function flatten(arr){
  var flat = [];

  arr.forEach(function(item){     
    flat.push.apply(flat, Array.isArray(item) ? flatten(item) : [item]);      
  });

  return flat;
}

Upvotes: 2

Related Questions