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);