Edited at

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

More than 3 years have passed since last update.

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}]}}