2

What would be the most elegant way to turn any array of (equal length) rows into an array of columns?

Eg:

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

# To    

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

This is what I have so far:

grid = [
  [1,2,3]
  [4,5,6]
]

grid2 = []

for i in grid[0]
  grid2.push []

for row, y in grid
  for el, x in row
    grid2[x].push el

Is there maybe even a 1-liner that would do it?

2
  • It would probably help to specify in the question what language you're looking for - Javascript or Coffeescript? Commented May 11, 2011 at 23:10
  • Either is fine really, as a concise solution in Javascript can be translated into an even more concise Coffeescript solution :) Commented May 11, 2011 at 23:13

4 Answers 4

7

In Javascript, if you are working in an environment with ECMAScript 5 array methods, the map() function works nicely for this:

var grid2 = grid[0].map(function(col, i) {
    return grid.map(function(row) {
        return row[i];
    });
});

This could be a one-liner if you killed the line breaks. :)

CoffeeScript:

grid[0].map (col, i) -> grid.map (row) -> row[i]
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for adding the Coffeescript - I'm less familiar with that syntax. It's worth noting that this seems to compile to much cleaner javascript code (pretty much exactly what I wrote) than the list comprehension version.
To support older environments, you could either modify the Array prototype or use underscore.js's _.map instead.
5

Don't use for..in with arrays where order is important!!

Using for..in with arrays has the following dangers:

  1. All enumerable properties of the array will be returned, including those on Array.prototype and the array itself so either you must be absolutely confident that no such extensions have occurred, or you must do hasOwnProperty and numeric index checks

  2. The order in which keys are returned is not guaranteed and can easily be disturbed - IE returns them in the order they are added, so if they are added out of order (e.g. using a decrementing while loop, which is quite common) they will returned in reverse order

Try the following in Firefox and IE:

var a = [0,1,2,3];
var b = [];
var i = a.length;
while (i--) {
  b[i] = a[i];
}
var s = [];
for (var p in b) {
  s.push(b[p]);
}

alert(b + '\n' + s);

// Firefox: 0,1,2,3
//          0,1,2,3

// IE: 0,1,2,3
//     3,2,1,0

Where order is important, only use loops where you explicitly access keys in the order you require. This also applies to objects, since in javascript the order in which properties are returned using for..in for all objects is implementation dependent and varies across browsers (noting that in javascript, everything is an object).

For..in is OK to use with arrays where the above issues are either not important or are dealt with. It is a handy tool for sparse arrays and accessing non-numeric enumerable properties.

A generic transpose function is:

function rows2cols(a) {
  var r = [];
  var t;

  for (var i=0, iLen=a.length; i<iLen; i++) {
    t = a[i];

    for (var j=0, jLen=t.length; j<jLen; j++) {
      if (!r[j]) {
        r[j] = [];
      }
      r[j][i] = t[j];
    }
  }
  return r;
}

It can be shortened and optimisied, but the above is a reasonably performant and easily maintained function.

5 Comments

This is good to know! If you wrote this in response to my answer, it's in CoffeeScript (I've updated the answer to make it less ambiguous).
CoffeeScript compiles to javascript, it doesn't address any of the issues noted above. It's the generated javascript that you need to worry about. Writing less code purely for the sake of it is a bad idea.
The javascript my snippet compiles to does not use "for .. in".. it iterates over each element in the array as your example does. Try pasting it into the "Try CoffeeScript" page here to see: jashkenas.github.com/coffee-script
The generated code doesn't correctly transpose the array (maybe I'm not using it correctly), it just returns the first row as a column. The code posted by nrabinowitz seems to work, but is dependant on map(), which may not be widely implemented. The plain for loop runs 4 times faster than using map anyway - I suspect it's all those extra function calls instead of plain property access (less code !== better). :-)
What I posted should work. Take a look at it running here: jsfiddle.net/5DfhN
2

Try this:

var new_grid = [];
for(var i = 0; i < grid[0].length; i++){
    new_grid.push([grid[0][i], grid[1][i]]); 
    // this is all under assumption that all the arrays are the same size
}

Would get you a result of:

new_grid = [
   [1,4],
   [2,5],
   [3,6],
]

1 Comment

Thanks for the response! I was hoping for something that would work for any size grid. I've clarified my question. I also found a 1-liner that works which I added as an answer.
2

CoffeeScript:

((row[i] for row in grid) for i in [0...grid[0].length])

1 Comment

I think you'd gain significant readability by caching len = grid[0].length beforehand and turning the one-liner into a two-liner with indentation, but yes, this likely the best approach.

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.