Mapping an array with possible subarrays as elements

Asked

Viewed 794 times

9

I am trying to create a function that files an array to another newArray. If one of the array elements is an array, I want to map all 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 is 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 ]

8 answers

9

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

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

Would that be:

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: http://jsfiddle.net/9zacjndf/

Another way more "gambiarra" could be 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: http://jsfiddle.net/9zacjndf/1/

Recursively:

If you need to use arrays with N precise depth levels of a recursive function. Here is a suggestion that appears to be 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: http://jsfiddle.net/nmou4yvz/1

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;
}
  • Hugo, can you tell me what’s wrong with my algorithm?

  • 2

    When typeof(element) === "object" the callback of map returns undefined because there is no return. The Return inside the foreach doesn’t count - it makes the foreach callback return, not the map callback. In short: you can’t solve this problem with magic, you have to do recursion anyway.

  • Your answer performs best among recursives: http://jsperf.com/flatten-de-array-em-javascript

  • 1

    @bfavaretto also added a recursive function: http://jsperf.com/flatten-de-array-em-javascript/2 Hugo: +1 for wasting my time improving my response :)

6

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

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

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

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

  • I would just add that the foreach will return one Array likewise, which does not give the effect he desires...

  • 1

    What do you mean? The forEach returns undefined (if the code were return element.forEach...).

  • you’re right, confused me because I gave the return in the element... :/

4

What you want to do seems to be a flat. For that just 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;
}

That should solve your problem.

  • 1

    Felipe, can you tell me what’s wrong with my algorithm?

  • 1

    @bfavaretto has already responded here

4


Turn the multidimensional array into a string, then turn it into an 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]
  • 4

    This approach does not work if the elements contain comma strings: flatten("a,b") --> ["a", "b"]

  • Simple solution to the problem by hand.

4

there are already some answers here, but I think there’s a more "clean":

function flatten (elemento){
  if(Array.isArray(elemento))
    return elemento.reduce(function(anterior,atual){
            return anterior.concat(flatten(atual))
          },[]);
  else return [elemento];
}
  • 1

    This function has quadratic complexity in the worst case, when the input has "depth" N: Something like [1,[2,[3,[4,[5]]]]]

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)

Use:

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

Exit:

[1, 2, 3, 4, 5, 1, 3, 2, 4, 1, 555, 555, 654, "I’m crazy!"]

Jsfiddle

2

Update 2021 You can use the method flat of array:

const array = [1,2,3,4,5, [1,3,2,4,1]];
console.log(array.flat());


flat

The method flat() creates a new array with all sub-arrays elements concatenated into it recursively to the specified depth.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.