0

I'm trying to understand how this snippet of codes work to flatten multi levels/nested array

can anyone please help to elaborate? thanks

var flatten = function(a, shallow,r){
  if(!r){ r = []}     //what does the exclamation mark mean here?...

if (shallow) {
  return r.concat.apply(r,a);  //I can't find what's .apply for concat method
  }

   for(var i=0; i<a.length; i++){
        if(a[i].constructor == Array){
            flatten(a[i],shallow,r);
        }else{
            r.push(a[i]);
        }
    }
    return r;
}

alert(flatten([1, [2], [3, [[4]]],[5,6]]));
Nate
  • 161
  • 1
  • 1
  • 12
  • Have you read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT_(!), https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply? – Bergi Jun 25 '15 at 05:55
  • Where did you find this code? – Bergi Jun 25 '15 at 05:55
  • 1
    @Bergi It's from [here](http://www.w3resource.com/javascript-exercises/javascript-array-exercise-21.php). – Ja͢ck Jun 25 '15 at 06:00
  • @Ja͢ck: Thanks, that site really fails to explain anything or provide instructive code, like that `r` stands for `result` or `.constructor == Array` is a horrible way to [check if object is an array](http://stackoverflow.com/questions/4775722/check-if-object-is-array)… – Bergi Jun 25 '15 at 06:05

3 Answers3

5

Interestingly the shallow part of the code is not reached in the example.

update

I think the function is hard to follow. Here's an alternate that might be easier to read.

function flatten(arr) {

  var flatArray = [];

  function pushLoop(a) {
    var len = a.length;
    var i=0;
    for (i; i < len; i++) {
      if (a[i] && a[i].constructor == Array) {
        pushLoop(a[i]);
      } else {
        flatArray.push(a[i]);
      }
    }
  }

  pushLoop(arr);
  return flatArray;
}

original snippit

var flatten = function(a, shallow, r) {

  // if r does not exist create.
  if (!r) {
    r = []
  }

  // if shallow exists concat the arrays
  if (shallow) {

    // concat joins two or more arrays as paramenters.
    // The prototype apply allows us to define the parameters
    // as an array.  Esentially concat the arrays in array a.
    return r.concat.apply(r, a);
  }


  for (var i = 0; i < a.length; i++) {
    // Only flatten items that are arrays and push everything
    // else to the end.
    if (a[i].constructor == Array) {
      flatten(a[i], shallow, r);
    } else {
      r.push(a[i]);
    }
  }
  return r;
}

var output = document.getElementById("output");

var arr = (flatten([1, [2],
  [3, [
    [4]
  ]],
  [5, 6]
]));

output.innerHTML = JSON.stringify(arr);
<div id="output"></div>
wolfhammer
  • 2,641
  • 1
  • 12
  • 11
1

!r is a negation as in "Not r". It means that the if state meant will be true if r=false, r=null, or r=undefined.

.apply() is a member of all functions in javascript. It allows you to call the function with a different "this" scope than the function that it's in. Check this out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

jwanga
  • 4,166
  • 4
  • 26
  • 27
0

About Apply

The line return r.concat.apply(r,a) is misleading.

They could have used [].concat.apply(r,a) as well. 'apply' is a method defined on every object of type 'function' (typeof o === 'function', essentially any callable object) that executes the function with 'this' bound to the first argument and the arguments in the array passed as the second argument.

So let's say we have a function 'f'.

    function f(a,b,c) {
      this.x = a + b + c;
    }

These two code blocks both call f where this === objInstance and after both blocks are run, objInstance.x = 6.

    var Obj = function() {};
    Obj.prototype.f = f;
    var objInstance = new Obj();
    objInstance.f(1,2,3);

    var Obj = function () {};
    var objInstance = new Obj();
    f.apply(objInstance, [1,2,3]);

So [].concat.apply(r,a) is equivalent to [].concat applied with arguments a on the object r. It concatenates every array in a to the array r.