Skip to content

Commit

Permalink
generalize transpose to arrays of arrays [[]], arrays of objects [{}]…
Browse files Browse the repository at this point in the history
…, objects of arrays {[]} and objects of objects {{}}.

closes #48
  • Loading branch information
Fil committed Jun 25, 2020
1 parent a286cfa commit 5186502
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ d3.range(49).map(function(d) { return d / 49; }); // GOOD: returns 49 elements.

<a name="transpose" href="#transpose">#</a> d3.<b>transpose</b>(<i>matrix</i>) · [Source](https://github.com/d3/d3-array/blob/master/src/transpose.js), [Examples](https://observablehq.com/@d3/d3-transpose)

Uses the [zip](#zip) operator as a two-dimensional [matrix transpose](http://en.wikipedia.org/wiki/Transpose).
A two-dimensional [matrix transpose](http://en.wikipedia.org/wiki/Transpose). Accepts matrices as arrays of arrays [[]], arrays of objects [{}], objects of arrays {[]} and objects of objects {{}}.

<a name="zip" href="#zip">#</a> d3.<b>zip</b>(<i>arrays…</i>) · [Source](https://github.com/d3/d3-array/blob/master/src/zip.js), [Examples](https://observablehq.com/@d3/d3-transpose)

Expand Down
35 changes: 25 additions & 10 deletions src/transpose.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import min from "./min.js";

export default function(matrix) {
if (!(n = matrix.length)) return [];
for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) {
for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {
row[j] = matrix[j][i];
// matrix is a key-value store of lines, themselves key-value stores of data.
// dimension y of the incoming matrix
const y = Object.keys(matrix);
if (!y.length) return [];

// dimension x of the incoming matrix
const line0 = matrix[y[0]],
x = new Set(Object.keys(line0)),
transpose = line0.length ? [] : {};

// prepare the transpose matrix with x as first dimension
for (const k of x) {
transpose[k] = matrix.length ? [] : {};
}
for (const [i, line] of Object.entries(matrix)) {
for (const k of x) {
// checks that each key is present in the line, otherwise:
// - remove that key from the transpose (for lines already read)
// - remove it from x (ignores it in future lines)
if (!(k in line)) {
delete transpose[k];
x.delete(k);
} else {
transpose[k][i] = line[k];
}
}
}
return transpose;
}

function length(d) {
return d.length;
}
26 changes: 26 additions & 0 deletions test/transpose-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,29 @@ tape("transpose(…) returns a copy", function(test) {
test.deepEqual(tranpose, [[1, 3], [2, 4]]);
test.end();
});

tape("transpose([objects]) transposes an array of objects", function(test) {
test.deepEqual(arrays.transpose([{a:1, b:2}, {a:3, b:4}, {a:5, b:6}]), {a: [1, 3, 5], b: [2, 4, 6]});
test.end();
});

tape("transpose([objects]) only uses properties present in all the objects", function(test) {
test.deepEqual(arrays.transpose([{a:1, b:2, c:-1}, {a:3, b:4}, {a:5, b:6, d:-1}]), {a: [1, 3, 5], b:[2, 4, 6]});
test.end();
});

tape("transpose(object) transposes an object of arrays", function(test) {
test.deepEqual(arrays.transpose({a: [1, 3, 5], b: [2, 4, 6]}), [{a:1, b:2}, {a:3, b:4}, {a:5, b:6}]);
test.end();
});

tape("transpose(object) ignores extra elements", function(test) {
test.deepEqual(arrays.transpose({a: [1, 3, 5], b: [2, 4, 6, 8]}), [{a:1, b:2}, {a:3, b:4}, {a:5, b:6}]);
test.end();
});

tape("transpose(object) transposes an object of objects", function(test) {
test.deepEqual(arrays.transpose({A: {a:1, b:2, c:-1}, B: {a:3, b:4}, C: {a:5, b:6, d:-1}}), { a: { A: 1, B: 3, C: 5 }, b: { A: 2, B: 4, C: 6 } });
test.end();
});

0 comments on commit 5186502

Please sign in to comment.