Mapping an array with possible subarrays as elements

8

I'm trying to create a function that maps an array to another newArray. If one of the array elements is a array , I want to map all of its subelements to newArray.

Therefore:

var array = [1,2,3,4,5, [1,3,2,4,1]]

Should return

  

[1,2,3,4,5,1,3,2,4,1]

My code looks like this:

var array = [1,2,3,4,5, [1,3,2,4,1]]

var newArray = array.map(function(element){

    if (typeof(element) === "object"){
        element.forEach(function(subElement){
            return(subElement);
        })
    } else {
        return(element);
    }

})

newArray;

But newArray returns:

  

[1, 2, 3, 4, 5, undefined]

    
asked by anonymous 18.06.2015 / 19:38

7 answers

3

Transform the multidimensional array into a string, then transform it into array:

var array = [1,2,3,4,5, [1,3,2,4,1]];
console.log(array.join().split(",")); // ["1", "2", "3", "4", "5", "1", "3", "2", "4", "1"]

If only numbers are required:

var array = [1,2,3,4,5, [1,3,2,4,1]];
var novoArray = array.join().split(",").map(function(item) {
    return parseInt(item);
});
console.log(novoArray); // [1, 2, 3, 4, 5, 1, 3, 2, 4, 1]
    
18.06.2015 / 20:39
8

In your code you are using .map() , but this method returns an array with the same number of elements, so it will not work because the final array you want will have more elements than the first.

You can use the "new" .reduce() to do this.

It would look like this:

var array = [1, 2, 3, 4, 5, [1, 3, 2, 4, 1]];
var novoArray = array.reduce(function (a, b) {
    return a.concat(b);
}, []);
console.log(novoArray); // dá [1, 2, 3, 4, 5, 1, 3, 2, 4, 1]

jsFiddle: link

Another more "gambiarra" way could look like this:

var array = [1, 2, 3, 4, 5, [1, 3, 2, 4, 1]];
var novoArray = [].concat.apply([], array)
console.log(novoArray); // [1, 2, 3, 4, 5, 1, 3, 2, 4, 1]

jsFiddle: link

Recursively:

If you need to use arrays with N precise depth levels of a recursive function. Here's a suggestion that looks the fastest :

function alisar(arr) {
    var novo = [];
    function redutor(arr, el) {
        if (Array.isArray(arr)) {
            while ((el = arr.pop()) || arr.length) redutor(el);
        } else {
            novo.push(arr);
        }
    }
    redutor(arr);
    return novo.reverse();
}

jsFiddle: link

    
18.06.2015 / 20:22
6

The most common name for this function you want is "flatten". For me, the easiest way to implement this function is with recursion.

function flatten(inputArray){
    var out = [];
    function go(x){
       if(Array.isArray(x)){
           for(var i=0; i<x.length; i++){
               go(x[i]);
           }
       }else{
           out.push(x);
       }
    }
    go(inputArray);
    return out;
}
    
18.06.2015 / 20:13
6

You already have several solutions in the other answers, so I'll just explain the problem with your code. The problem is this:

element.forEach(function(subElement){
    return(subElement);
})

You have a return within the forEach al callback, but this return does not apply to the map callback as you seem to expect. That way, when your code follows the typeof(element) === "object" path, the map callback has no set return. And in JS functions without return implicitly return undefined , which is what is going to stop in your result.

As I do not have a solution to add, I take the space to say that among the answers with recursion presented so far, @hugomg is the one with the best performance .

    
18.06.2015 / 20:33
4

What you want to do seems to be flat . To do this simply do the following:

function flat(arr){
    var ret = [];
    for(var i = 0; i < arr.length; i++){
        if(arr[i] instanceof Array){
            ret = ret.concat(flat(arr[i]));
        }else{
            ret.push(arr[i]);
        }
    }
    return ret;
}

This should solve your problem.

    
18.06.2015 / 20:24
4

You can create your own mapping:

Array.prototype.hasObject = function(){
    for (i in this)
        if(typeof this[i] == "object")
            return true;

    return false;
}

Array.prototype.flatten  = function(){
    if (typeof condition == 'undefined') 
        condition = function(){return true;};

    narray = [];

    this.forEach(function(element){
        if (typeof element == 'object'){
            for(i in element){
                if (!element.hasOwnProperty(i))
                    continue;
                narray.push(element[i]);
            }
        } else 
            narray.push(element);
    });
    
    if (narray.hasObject())
        return narray.flatten();
    else return narray;
}
var array = [1,2,3,4,5, [1,3,2,4,1, [555,555,654, ['eu to malucooooo!']]]];
var newArray = array.flatten();

console.log(newArray)

Usage:

var array = [1,2,3,4,5, [1,3,2,4,1, [555,555,654, ['eu to malucooooo!']]]];
var newArray = array.flatten();

Output:

  

[1, 2, 3, 4, 5, 1, 3, 2, 4, 1, 555, 555, 654, "I am a malucooooo!"]

JsFiddle

    
18.06.2015 / 21:09
3

I already have some answers here, but I think it has a more "clean" form:

function flatten (elemento){
  if(Array.isArray(elemento))
    return elemento.reduce(function(anterior,atual){
            return anterior.concat(flatten(atual))
          },[]);
  else return [elemento];
}
    
18.06.2015 / 20:40