4

I've developed the following code (http://codepen.io/PiotrBerebecki/pen/KrkdPj) when trying to flatten a multidimensional array into a one-dimensional array.

For example,

flattenArray([1, [2, 3], 4, [5, [6]], 7])
// should return [1, 2, 3, 4, 5, 6, 7]


// Flatten an array
function flattenArray(input) {
  return input.reduce(function(prev, curr) {
    if (Array.isArray(curr)) {return prev.concat(flattenArray(curr));}
    else {return prev.concat(curr);}
  }, []);
}

console.log(  flattenArray([1, [2, 3], 4, [5, [6]], 7])  );
// [1, 2, 3, 4, 5, 6, 7]

The above seems to work OK.

Now, I've been asked to make another function that instead of an array will achieve the same result for a mix of numbers and arrays.

For example,

flattenMix(1, [2, 3], 4, [5, [6]], 7);
// should return [1, 2, 3, 4, 5, 6, 7]

I've slightly modified the above by adding the rest parameter so that the function can accept an arbitrary number of arguments. However I'm getting a maximum call stack error. Would you know what the problem is with the function below?

// Flatten arguments (arbitrary length) consisting of a mix of numbers and arrays
function flattenMix(...input) {
  return input.reduce(function(prev, curr) {
    if (Array.isArray(curr)) {return prev.concat(flattenMix(curr));}
    else {return prev.concat(curr);}
  }, []);
}

console.log(  flattenMix(1, [2, 3], 4, [5, [6]], 7)  ); // should return [1, 2, 3, 4, 5, 6, 7]

UPDATE 1

The answer and suggestions below have indicated that I should be using the ... spread operator in the recursive call flattenMix(...curr). In this way when the curr is an array (as tested) then the containing elements of the curr would be passed in to the flattenMix function (instead of the curr array).

5
  • 1
    Your if statement must be resolving to true all the time. Use a tool (like Chrome Dev Tools) and step through the code to find out why this is. Commented Jul 14, 2016 at 11:04
  • 2
    Try prev.concat(flattenMix(...curr)); - but follow evolutionxbox's advice, it will help you immensely with debugging. Commented Jul 14, 2016 at 11:12
  • @le_m, your suggestion has indeed fixed the issue. If you add it as an answer, I would accept it as an solution. Commented Jul 14, 2016 at 11:17
  • @PiotrBerebecki I am on mobile, so writing an answer is bothersome (no stack snippets etc). Better accept Nina's answer, hers is equally correct. Commented Jul 14, 2016 at 11:25
  • Related: Spread Syntax vs Rest Parameter in ES2015 / ES6. Commented Mar 16, 2021 at 12:07

4 Answers 4

2

Well, you could just use the spread operator.

function flattenMix(...input) {
    return input.reduce(function (prev, curr) {
        if (Array.isArray(curr)) {
            return prev.concat(flattenMix(...curr));
            //                            ^^^^^^^
        } else {
            return prev.concat(curr);
        }
    }, []);
}

console.log(flattenMix(1, [2, 3], 4, [5, [6]], 7));

Sign up to request clarification or add additional context in comments.

1 Comment

@Piotr Just to clarify it: ... in a function declaration is called rest operator. Used within a function call it is called spread operator and causes the exact opposite of rest.
2

Well, easy. Your first method can already deal with an array and since arguments is basically an array we can just add an extra line.

// Flatten an array
function flattenArray(input) {
  return input.reduce(function(prev, curr) {
    if (Array.isArray(curr)) {
      return prev.concat(flattenArray(curr));
    } else {
      return prev.concat(curr);
    }
  }, []);
}

function flattenMix() {
  return flattenArray([].slice.call(arguments));
}

console.log(flattenMix(1, [2, 3], 4, [5, [6]], 7));

1 Comment

The rest operator is exactly the replacement of arguments in ES2015.
1

Your problem is that you send an array as first argument in recursion instead of array items. You can use flatten.apply(null, arr) or flatten(...arr):

// Flatten arguments (arbitrary length) consisting of a mix of numbers and arrays

function flatten(...input) {
  return input.reduce(function(prev, curr) {
    return prev.concat(
      Array.isArray(curr) ? flatten(...curr) : curr
    );
  }, []);
}

console.log(flatten(1, [2, 3], 4, [5, [6]], 7)); // should return [1, 2, 3, 4, 5, 6, 7]

Do it the fun way

If you convert an array of array to string this would flatten the array for you and the only thing you should do is to convert string numbers to Number.

function flattenArgs() {
  return Array.from(arguments).toString()
    .split(',').map(Number);
}

console.log(flattenArgs(1, [2, 3], 4, [5, [6]], 7));

6 Comments

@nnnnnn What you mean by that? I don't get it.
I mean that you've not explained why the OP's code doesn't work, you've given a completely different solution.
That's much better as your little hack. Please replace apply with the spread operator though, because this is exactly the purpose of spread.
@LUH3417 I've explained both are applicable. BTW for fun of modern JS I did it too.
As you know apply was originally introduced to set the implicit this parameter directly (and to spread the items of an array to the given function of course). The spread operator...just spreads and thus fits better.
|
0

There is a new method Array.flat()

var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.