サンプルで確認するD3の配列メソッド


D3.js には便利な関数がたくさんある感じなんですけど、 [英語のページ](https://github.com/mbostock/d3/wiki/Arrays#ordering) をみてもよく分からんし  [日本語の解説ページ](http://blog.n1n9.jp/how2/d3-array.php) を見ても難しくて分からん。ってことで、サンプル動かして動作を確認してみましたよ。

やっぱりデータ見たほうがわかりやすいですね。

全体的な感じからすると、`Undefined` を許容したりしなかったりソートされてることが前提って関数が多いんで、配列操作のときは事前にソートと値なしの排除をしとくのが確かっぽいですね。
ちなみに `Array.sort()` だと辞書順にならんでしまいますので、 `d3.ascending()` を使うのが確実です。

よく使いそうな関数には☆をつけて、ハマリポイントがありそうな関数には注釈のコメント入れてます。

# D3: ARRAY METHODS

## Ordering

### d3.ascending(a, b) ☆

```js
var a = 10,
    b = 1;
p(d3.ascending(a, b)); // return: 1
var c = undefined;
p(d3.ascending(a, c)); // return: NaN
var d = "-";
p(d3.ascending(a, d)); // return: NaN
```

### d3.descending(a, b) ☆

```js
var a = 10,
    b = 2;
p(d3.descending(a, b)); // return: -1
```

### d3.min(array[, accessor])

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.min(a)); // return: 0
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.min(a, function(d, i) {
  return parseInt(d) + 1;
})); // return: 1
```

### d3.max(array[, accessor])

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.max(a)); // return: 10
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.max(a, function(d, i) {
  return parseInt(d) + i;
})); // return: 14
```

### d3.extent(array[, accessor]) ☆

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.extent(a)); // return: 0,10
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.extent(a, function(d, i) {
  return parseInt(d) + i;
})); // return: 0,14
```

### d3.sum(array[, accessor]) ☆

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.sum(a)); // return: 15
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.sum(a, function(d, i) {
  return parseInt(d) + i;
})); // return: 28
```

### d3.mean(array[, accessor]) ☆

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.mean(a)); // return: 3
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.mean(a, function(d, i) {
  return parseInt(d) + i;
})); // return: 5.6
```

### d3.median(array[, accessor]) ☆

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.median(a)); // return: 2
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.median(a, function(d, i) {
  return parseInt(d) + i;
})); // return: 5
```

### d3.quantile(numbers, p)
```js
var a = ["0", 1, undefined, 2, 10, "2"];
a.sort();
p(a);                        // return: ["0",1,10,"2",2,null]
p(d3.quantile(a, 0.5));      // return: 6
a.sort(function(a, b) {
  return d3.ascending(a, b);
});
p(a);                        // return: ["0",1,"2",2,10,null]
p(d3.quantile(a, 0.5));      // return: 2
```

```js
var a = ["0", 1, undefined, 2, 10, "2"];
a.sort();
p(a);                        // return: ["0",1,10,"2",2,null]
p(d3.quantile(a, 0.75));     // return: 2
a.sort(function(a, b) {
  return d3.ascending(a, b);
});
p(a);                        // return: ["0",1,"2",2,10,null]
p(d3.quantile(a, 0.75));     // return: 8
```

### d3.bisecLeft(array, x[, lo[, hi]])

```js
var a = ["0", 1, undefined, 2, 10, "2"];
a.sort();
p(a);                         // return: ["0",1,10,"2",2,null]
p(d3.bisectLeft(a, 3));       // return: 5
a.sort(function(a, b) {
  return d3.ascending(a, b);
});
p(a);                         // return: ["0",1,"2",2,10,null]
p(d3.bisectLeft(a, 3));       // return: 4
p(d3.bisectLeft(a, 3, 2, 3)); // return: 3
```

### d3.bisect(array, x[, lo[, hi]]), d3.bisectRight(array, x[, lo[, hi]])

右側(後ろ)から調べてるようで配列の最後に `Undefined` があると期待した値が返ってきません。

```js
var a = ["0", 1, undefined, 2, 10, "2"];
a.sort();
p(a);                              // return: ["0",1,10,"2",2,null]
p(d3.bisect(a, 3));                // return: 6
p(d3.bisectRight(a, 3));           // return: 6
a.sort(function(a, b) {
  return d3.ascending(a, b);
});
p(a);                              // return: ["0",1,"2",2,10,null]
p(d3.bisect(a, 3));                // return: 6
p(d3.bisectRight(a, 3));           // return: 6
p(d3.bisect(a, 2));                // return: 6
p(d3.bisectRight(a, 2));           // return: 6
var b = a.filter(function(d, i) {
  return d != undefined;
});
p(b);                              // return: ["0",1,"2",2,10]
p(d3.bisect(b, 2));                // return: 4
p(d3.bisectRight(b, 2));           // return: 4
p(d3.bisect(b, 1));                // return: 2
p(d3.bisectRight(b, 1));           // return: 2
p(d3.bisect(b, 1, 3, 4));          // return: 3
p(d3.bisectRight(b, 1, 3, 4));     // return: 3
```

### d3.bisector(accessor)

右側(後ろ)から調べてるようで配列の最後に `Undefined` があると期待した値が返ってきません。

```js
var bisect = d3.bisector(function(d) { return d; }).right;
var bisectLeft = d3.bisector(function(d) { return d; }).left;
var a = ["0", 1, undefined, 2, 10, "2"];
p(a);                             // return: ["0",1,null,2,10,"2"]
p(bisect(a, 2));                  // return: 6
a.sort();
p(a);                             // return: ["0",1,10,"2",2,null]
p(bisect(a, 2));                  // return: 6
a.sort(function(a, b) {
  return d3.ascending(a, b);
});
p(a);                             // return: ["0",1,"2",2,10,null]
p(bisect(a, 2));                  // return: 6
var b = a.filter(function(d, i) {
  return d != undefined;
});
p(b);                             // return: ["0",1,"2",2,10]
p(bisect(b, 2));                  // return: 4
p(bisectLeft(b, 2));              // return: 2
p(bisect(b, 3));                  // return: 4
p(bisectLeft(b, 3));              // return: 4
```

### d3.bisector(comparator)

右側(後ろ)から調べてるようで配列の最後に `Undefined` があると期待した値が返ってきません。

```js
var bisect = d3.bisector(function(d) { return d; }).right;
var bisectComparator = d3.bisector(function(a, b) { return b - a; }).right;
var a = ["0", 1, undefined, 2, 10, "2"];
a.sort(function(a, b) {
  return d3.ascending(a, b);
});
var b = a.filter(function(d, i) {
  return d != undefined;
});
p(b);                      // return: ["0",1,"2",2,10]
p(bisect(b, 2));           // return: 4
p(bisectComparator(b, 2)); // return: 5
```

### d3.shuffle(array)

```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.shuffle(a)); // return: ["0",1,"2",10,null,2]
p(d3.shuffle(a)); // return: [null,1,10,"2",2,"0"]
p(d3.shuffle(a)); // return: [null,2,"2",10,"0",1]
```

## Associative Arrays

### d3.keys(object)

```js
var o = {
  b: 54,
  a: 30,
  c: 12
};
p(o);          // return: return: {"b":54,"a":30,"c":12}
p(d3.keys(o)); // return: ["b","a","c"]
```

### d3.values(object)

```js
var o = {
  b: 54,
  a: 30,
  c: 12
};
p(o);            // return: return: {"b":54,"a":30,"c":12}
p(d3.values(o)); // return: [54,30,12]
```

### d3.entries(object)

```js
var o = {
  b: [54, 34, 89],
  a: [30, 34, 59],
  c: [30, 12, 12]
};
p(o);             // return: {"b":[54,34,89],"a":[30,34,59],"c":[30,12,12]}
p(d3.entries(o)); // return: [{"key":"b","value":[54,34,89]},{"key":"a","value":[30,34,59]},{"key":"c","value":[30,12,12]}]
```

## Maps

### d3.map([object[, key])

キーを指定しないと数字の添字になってしまいますので、キーを指定する関数は必ず入れたほうがいいでしょう。
また、重複した要素があるときにキーを指定した時としない時では結果が違います。

```js
var o = d3.map([{name: "foo"}, {name: "bar"}]);
p(o); // {"_":{"0":{"name":"foo"},"1":{"name":"bar"}}}
var m = d3.map([{name: "foo"}, {name: "bar"}, {name: "bar"}]);
p(m); // {"_":{"0":{"name":"foo"},"1":{"name":"bar"},"2":{"name":"bar"}}}
    m = d3.map([{name: "foo"}, {name: "bar"}, {name: "bar"}], function(d) { return d.name; });
p(m); // {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
```

### map.has(key)
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);            // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.has("foo")); // return: true
p(m.has("baz")); // return: false
```

### map.get(key)
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);            // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.get("foo")); // return: {"name":"foo"}
p(m.get("baz")); // return: undefined
```

### map.set(key)
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m); // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
m.set("foo", {"name": "test"});
m.set("baz", {"name": "foo"});
p(m); // return: {"_":{"foo":{"name":"test"},"bar":{"name":"bar"},"baz":{"name":"foo"}}}
```

### map.remove(key)
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m); // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
m.remove("foo");
p(m); // return: {"_":{"bar":{"name":"bar"}}}
```

### map.keys()
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);        // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.keys()); // return: ["foo","bar"]
```

### map.values()
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);          // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.values()); // return: [{"name":"foo"},{"name":"bar"}]
```

### map.entries()
```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);           // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.entries()); // return: [{"key":"foo","value":{"name":"foo"}},{"key":"bar","value":{"name":"bar"}}]
```

### map.forEach(function)

```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);        // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
m.forEach(function(k, v) {
  p([k, v]); // return: ["foo",{"name":"foo"}], return: ["bar",{"name":"bar"}]
});
```

### map.empty()

```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);                  // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.empty());          // return: false
p(d3.map([]).empty()); // return: true
```

### map.size()

```js
var m = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
p(m);                 // return: {"_":{"foo":{"name":"foo"},"bar":{"name":"bar"}}}
p(m.size());          // return: 2
p(d3.map([]).size()); // return: 0
```

## Sets

### d3.set([array]) ☆

配列の深さが違う場合にもゴニョゴニョしてくれる感じですけど、カンマ区切りの値になってしまうようです。

```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a); // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
```
```js
var a = d3.set([["foo"], ["bar", "baz"]]);
p(a);     // return: {"_":{"foo":true,"bar,baz":true}}
```

### set.has(value)
```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);        // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
p(a.has(2)); // return: true
p(a.has(3)); // return: false
```

### set.add(value) ☆
```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);     // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
a.add(5);
p(a);     // return: {"_":{"0":true,"1":true,"2":true,"5":true,"10":true,"undefined":true}}
```

### set.remove(value)
```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);     // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
a.remove(1);
p(a);     // return: {"_":{"0":true,"2":true,"10":true,"undefined":true}}
```

### set.values() ☆

```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);          // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
p(a.values()); // return: ["0","1","2","10","undefined"]
```

### set.forEach(function)


```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);          // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
a.forEach(function(v) {
    p(v); // return: "0", return: "1", return: "2", return: "10", return: "undefined"
});
```

### set.empty()
```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);                  // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
p(a.empty());          // return: false
p(d3.set([]).empty()); // return: true
```

### set.size()
```js
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a);                 // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
p(a.size());          // return: 5
p(d3.set([]).size()); // return: 0
```

## Array Operators

### d3.merge(arrays)
```js
var a = d3.merge([ [1], [2, 3] ]);
p(a); // return: [1,2,3]
```
```js
var a = d3.merge([ [1], [2, 3], [5, [2,3]] ]);
p(a); // return: [1,2,3,5,[2,3]]
```

### d3.range([start, ]stop[, step]) ☆

start <= range < stop という関係のようですのでちょっと注意が必要です。

```js
var a = d3.range(5);
p(a);                     // return: [0,1,2,3,4]
p(d3.range(10, 100, 10)); // return: [10,20,30,40,50,60,70,80,90]
```

### d3.permute(array, indexes) ☆
```js
var a = ["0", 1, undefined, 2, 10, "2"];
p(a);                        // return: ["0", 1, undefined, 2, 10, "2"]
p(d3.permute(a, [2, 5, 1])); // return: [null,"2",1]
```
```js
var a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
p(a);                             // return: [0,1,2,3,4,5,6,7,8,9]
p(d3.permute(a, d3.range(3, 6))); // return: [3,4,5]
```

### d3.zip(arrays...) ☆
要素数が足りない場合は配列が作成されない模様。

```js
var a = d3.zip(["a", "b"], [1, 6], [2, 7], [3, 8]);
p(a); // return: [["a",1,2,3],["b",6,7,8]]
```
```js
var a = d3.zip(["a", "b"], [1, 6, 10], [2, 7], [3, 8]);
p(a); // return: [["a",1,2,3],["b",6,7,8]]
```
```js
var a = d3.zip(["a", "b", "c"], [1, 6], [2, 7], [3, 8]);
p(a); // return: [["a",1,2,3],["b",6,7,8]]
```

### d3.transpose(matrix)
```js
var a = d3.transpose([["a", 1, 11], ["b", 2, 12], ["c", 3, 13]]);
p(a); // return: [["a","b","c"],[1,2,3],[11,12,13]]
```

### d3.d3.pairs(array)
```js
var a = d3.pairs(["0", 1, undefined, 2, 10, "2"]);
p(a); // return: [["0",1],[1,null],[null,2],[2,10],[10,"2"]]
```

## Nest

### d3.nest()
```js
var n = d3.nest();
p(n); // return: {}
```

### nest.entries(array)
```js
var o = [
  {"key": "foo", "val": 10},
  {"key": "bar", "val": 30},
  {"key": "baz", "val": 20}
];
var n = d3.nest()
          .entries(o);
p(n); // return: [{"key":"foo","val":10},{"key":"bar","val":30},{"key":"baz","val":20}]
```

### nest.key(function)
```js
var o = [
  {"key": "foo", "val": 10},
  {"key": "bar", "val": 30},
  {"key": "bar", "val": 15},
  {"key": "baz", "val": 20}
];
var n = d3.nest()
          .key(function(d) {return d.key; })
          .entries(o);
p(n); // return: [{"key":"foo","values":[{"key":"foo","val":10}]},{"key":"bar","values":[{"key":"bar","val":30},{"key":"bar","val":15}]},{"key":"baz","values":[{"key":"baz","val":20}]}]
```

### nest.sortKeys(comparator) ☆
```js
var o = [
  {"key": "foo", "val": 10},
  {"key": "bar", "val": 30},
  {"key": "baz", "val": 20}
];
var n = d3.nest()
          .key(function(d) {return d.key; })
          .sortKeys(d3.ascending)
          .entries(o);
p(n); // return: [{"key":"bar","values":[{"key":"bar","val":30}]},{"key":"baz","values":[{"key":"baz","val":20}]},{"key":"foo","values":[{"key":"foo","val":10}]}]
```

### nest.sortValues(comparator)
```js
var o = [
  {"key": "foo", "val": 10},
  {"key": "bar", "val": 30},
  {"key": "bar", "val": 15},
  {"key": "baz", "val": 20}
];
var n = d3.nest()
          .key(function(d) {return d.key; })
          .sortValues(function(a, b) {p([a, b]); return d3.ascending(a.val, b.val);})
          .entries(o);
p(n); // return: [{"key":"foo","values":[{"key":"foo","val":10}]},{"key":"bar","values":[{"key":"bar","val":15},{"key":"bar","val":30}]},{"key":"baz","values":[{"key":"baz","val":20}]}]
```

### nest.rollup(function) ☆

```js
var o = [
  {"key": "foo", "val": 10},
  {"key": "bar", "val": 30},
  {"key": "bar", "val": 15},
  {"key": "baz", "val": 20}
];
var n = d3.nest()
          .key(function(d) {return d.key; })
          .sortKeys(d3.ascending)
          .rollup(function(d) {
            var val = 0;
            d.forEach(function(v) {
              val += v.val;
            });
            return val;
          })
          .entries(o);
p(n); // return: [{"key":"bar","values":45},{"key":"baz","values":20},{"key":"foo","values":10}]
```

### nest.map(array[, mapType])
```js
var o = [
  {"key": "foo", "val": 10},
  {"key": "bar", "val": 30},
  {"key": "baz", "val": 20}
];
var n = d3.nest()
          .key(function(d) {return d.key; })
          .map(o, d3.map);
p(n); // return: {"_":{"foo":[{"key":"foo","val":10}],"bar":[{"key":"bar","val":30}],"baz":[{"key":"baz","val":20}]}}
```