D3.js には便利な関数がたくさんある感じなんですけど、 英語のページ をみてもよく分からんし 日本語の解説ページ を見ても難しくて分からん。ってことで、サンプル動かして動作を確認してみましたよ。
やっぱりデータ見たほうがわかりやすいですね。
全体的な感じからすると、Undefined
を許容したりしなかったりソートされてることが前提って関数が多いんで、配列操作のときは事前にソートと値なしの排除をしとくのが確かっぽいですね。
ちなみに Array.sort()
だと辞書順にならんでしまいますので、 d3.ascending()
を使うのが確実です。
よく使いそうな関数には☆をつけて、ハマリポイントがありそうな関数には注釈のコメント入れてます。
D3: ARRAY METHODS
Ordering
d3.ascending(a, b) ☆
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) ☆
var a = 10,
b = 2;
p(d3.descending(a, b)); // return: -1
d3.min(array[, accessor])
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.min(a)); // return: 0
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])
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.max(a)); // return: 10
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]) ☆
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.extent(a)); // return: 0,10
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]) ☆
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.sum(a)); // return: 15
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]) ☆
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.mean(a)); // return: 3
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]) ☆
var a = ["0", 1, undefined, 2, 10, "2"];
p(d3.median(a)); // return: 2
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)
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
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]])
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
があると期待した値が返ってきません。
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
があると期待した値が返ってきません。
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
があると期待した値が返ってきません。
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)
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)
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)
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)
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])
キーを指定しないと数字の添字になってしまいますので、キーを指定する関数は必ず入れたほうがいいでしょう。
また、重複した要素があるときにキーを指定した時としない時では結果が違います。
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)
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)
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)
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)
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()
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()
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()
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)
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()
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()
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]) ☆
配列の深さが違う場合にもゴニョゴニョしてくれる感じですけど、カンマ区切りの値になってしまうようです。
var a = d3.set(["0", 1, undefined, 2, 10, "2"]);
p(a); // return: {"_":{"0":true,"1":true,"2":true,"10":true,"undefined":true}}
var a = d3.set([["foo"], ["bar", "baz"]]);
p(a); // return: {"_":{"foo":true,"bar,baz":true}}
set.has(value)
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) ☆
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)
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() ☆
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)
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()
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()
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)
var a = d3.merge([ [1], [2, 3] ]);
p(a); // return: [1,2,3]
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 という関係のようですのでちょっと注意が必要です。
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) ☆
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]
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...) ☆
要素数が足りない場合は配列が作成されない模様。
var a = d3.zip(["a", "b"], [1, 6], [2, 7], [3, 8]);
p(a); // return: [["a",1,2,3],["b",6,7,8]]
var a = d3.zip(["a", "b"], [1, 6, 10], [2, 7], [3, 8]);
p(a); // return: [["a",1,2,3],["b",6,7,8]]
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)
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)
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()
var n = d3.nest();
p(n); // return: {}
nest.entries(array)
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)
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) ☆
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)
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) ☆
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])
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}]}}