var vows = require("vows"), _ = require("../../"), load = require("../load"), assert = require("../assert"); var suite = vows.describe("d3.nest"); suite.addBatch({ "entries": { topic: load("arrays/nest").expression("d3.nest"), "returns an array of each distinct key in arbitrary order": function(nest) { var keys = nest() .key(function(d) { return d.foo; }) .entries([{foo: 1}, {foo: 1}, {foo: 2}]) .map(function(d) { return d.key; }) .sort(_.ascending); assert.deepEqual(keys, ["1", "2"]); }, "each entry is a key-values object, with values in input order": function(nest) { var entries = nest() .key(function(d) { return d.foo; }) .entries([{foo: 1, bar: 0}, {foo: 2}, {foo: 1, bar: 1}]); assert.deepEqual(entries, [ {key: "1", values: [{foo: 1, bar: 0}, {foo: 1, bar: 1}]}, {key: "2", values: [{foo: 2}]} ]); }, "keys can be sorted using an optional comparator": function(nest) { var keys = nest() .key(function(d) { return d.foo; }).sortKeys(_.descending) .entries([{foo: 1}, {foo: 1}, {foo: 2}]) .map(function(d) { return d.key; }); assert.deepEqual(keys, ["2", "1"]); }, "values can be sorted using an optional comparator": function(nest) { var entries = nest() .key(function(d) { return d.foo; }) .sortValues(function(a, b) { return a.bar - b.bar; }) .entries([{foo: 1, bar: 2}, {foo: 1, bar: 0}, {foo: 1, bar: 1}, {foo: 2}]); assert.deepEqual(entries, [ {key: "1", values: [{foo: 1, bar: 0}, {foo: 1, bar: 1}, {foo: 1, bar: 2}]}, {key: "2", values: [{foo: 2}]} ]); }, "values can be aggregated using an optional rollup": function(nest) { var entries = nest() .key(function(d) { return d.foo; }) .rollup(function(values) { return _.sum(values, function(d) { return d.bar; }); }) .entries([{foo: 1, bar: 2}, {foo: 1, bar: 0}, {foo: 1, bar: 1}, {foo: 2}]); assert.deepEqual(entries, [ {key: "1", values: 3}, {key: "2", values: 0} ]); }, "multiple key functions can be specified": function(nest) { var entries = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .entries([[0, 1], [0, 2], [1, 1], [1, 2], [0, 2]]); assert.deepEqual(entries, [ {key: "0", values: [ {key: "1", values: [[0, 1]]}, {key: "2", values: [[0, 2], [0, 2]]} ]}, {key: "1", values: [ {key: "1", values: [[1, 1]]}, {key: "2", values: [[1, 2]]} ]} ]); }, "the rollup function only applies to leaf values": function(nest) { var entries = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .rollup(function(values) { return values.length; }) .entries([[0, 1], [0, 2], [1, 1], [1, 2], [0, 2]]); assert.deepEqual(entries, [ {key: "0", values: [ {key: "1", values: 1}, {key: "2", values: 2} ]}, {key: "1", values: [ {key: "1", values: 1}, {key: "2", values: 1} ]} ]); }, "the value comparator only applies to leaf values": function(nest) { var entries = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .sortValues(function(a, b) { return a[2] - b[2]; }) .entries([[0, 1], [0, 2, 1], [1, 1], [1, 2], [0, 2, 0]]); assert.deepEqual(entries, [ {key: "0", values: [ {key: "1", values: [[0, 1]]}, {key: "2", values: [[0, 2, 0], [0, 2, 1]]} ]}, {key: "1", values: [ {key: "1", values: [[1, 1]]}, {key: "2", values: [[1, 2]]} ]} ]); }, "the key comparator only applies to the last-specified key": function(nest) { var entries = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.descending) .entries([[0, 1], [0, 2], [1, 1], [1, 2], [0, 2]]); assert.deepEqual(entries, [ {key: "0", values: [ {key: "2", values: [[0, 2], [0, 2]]}, {key: "1", values: [[0, 1]]} ]}, {key: "1", values: [ {key: "2", values: [[1, 2]]}, {key: "1", values: [[1, 1]]} ]} ]); var entries = nest() .key(function(d) { return d[0]; }).sortKeys(_.descending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .entries([[0, 1], [0, 2], [1, 1], [1, 2], [0, 2]]); assert.deepEqual(entries, [ {key: "1", values: [ {key: "1", values: [[1, 1]]}, {key: "2", values: [[1, 2]]} ]}, {key: "0", values: [ {key: "1", values: [[0, 1]]}, {key: "2", values: [[0, 2], [0, 2]]} ]} ]); }, "if no keys are specified, the input array is returned": function(nest) { var array = [new Object()]; assert.strictEqual(nest().entries(array), array); } } }); suite.addBatch({ "map": { topic: load("arrays/nest").expression("d3.nest"), "returns a map of each distinct key": function(nest) { var map = nest() .key(function(d) { return d.foo; }) .map([{foo: 1, bar: 0}, {foo: 2}, {foo: 1, bar: 1}]); assert.deepEqual(map, { "1": [{foo: 1, bar: 0}, {foo: 1, bar: 1}], "2": [{foo: 2}] }); }, "values can be sorted using an optional comparator": function(nest) { var map = nest() .key(function(d) { return d.foo; }) .sortValues(function(a, b) { return a.bar - b.bar; }) .map([{foo: 1, bar: 2}, {foo: 1, bar: 0}, {foo: 1, bar: 1}, {foo: 2}]); assert.deepEqual(map, { "1": [{foo: 1, bar: 0}, {foo: 1, bar: 1}, {foo: 1, bar: 2}], "2": [{foo: 2}] }); }, "values can be aggregated using an optional rollup": function(nest) { var map = nest() .key(function(d) { return d.foo; }) .rollup(function(values) { return _.sum(values, function(d) { return d.bar; }); }) .map([{foo: 1, bar: 2}, {foo: 1, bar: 0}, {foo: 1, bar: 1}, {foo: 2}]); assert.deepEqual(map, { "1": 3, "2": 0 }); }, "multiple key functions can be specified": function(nest) { var map = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .map([[0, 1], [0, 2], [1, 1], [1, 2], [0, 2]]); assert.deepEqual(map, { "0": { "1": [[0, 1]], "2": [[0, 2], [0, 2]] }, "1": { "1": [[1, 1]], "2": [[1, 2]] } }); }, "the rollup function only applies to leaf values": function(nest) { var map = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .rollup(function(values) { return values.length; }) .map([[0, 1], [0, 2], [1, 1], [1, 2], [0, 2]]); assert.deepEqual(map, { "0": { "1": 1, "2": 2 }, "1": { "1": 1, "2": 1 } }); }, "the value comparator only applies to leaf values": function(nest) { var map = nest() .key(function(d) { return d[0]; }).sortKeys(_.ascending) .key(function(d) { return d[1]; }).sortKeys(_.ascending) .sortValues(function(a, b) { return a[2] - b[2]; }) .map([[0, 1], [0, 2, 1], [1, 1], [1, 2], [0, 2, 0]]); assert.deepEqual(map, { "0": { "1": [[0, 1]], "2": [[0, 2, 0], [0, 2, 1]] }, "1": { "1": [[1, 1]], "2": [[1, 2]] } }); }, "if no keys are specified, the input array is returned": function(nest) { var array = [new Object()]; assert.strictEqual(nest().map(array), array); }, "handles keys that are built-in prototype properties": function(nest) { var map = nest() .key(String) .map(["hasOwnProperty"]); // but note __proto__ wouldn’t work! assert.deepEqual(map, {hasOwnProperty: ["hasOwnProperty"]}); }, "a custom map implementation can be specified": function(nest) { var map = nest() .key(String) .map(["hasOwnProperty", "__proto__"], _.map); assert.deepEqual(map.entries(), [ {key: "hasOwnProperty", value: ["hasOwnProperty"]}, {key: "__proto__", value: ["__proto__"]} ]); }, "the custom map implementation works on multiple levels of nesting": function(nest) { var map = nest() .key(function(d) { return d.foo; }) .key(function(d) { return d.bar; }) .map([{foo: 42, bar: "red"}], _.map); assert.deepEqual(map.keys(), ["42"]); assert.deepEqual(map.get("42").keys(), ["red"]); assert.deepEqual(map.get("42").values(), [[{foo: 42, bar: "red"}]]); assert.deepEqual(map.get("42").entries(), [{key: "red", value: [{foo: 42, bar: "red"}]}]); } } }); suite.export(module);