d3-collection
几种常用的键值对类型的数据结构
Installing
NPM
安装:npm install d3-collection
, 还可以下载最新版,此外还可以直接从 d3js.org 以 单独的标准库 或作为 D3 4.0 的一部分引入,支持 AMD
, CommonJS
以及 vanilla
环境, 使用标签引入会暴露 d3
全局变量:
<script src="https://d3js.org/d3-collection.v1.min.js"></script>
<script>
var map = d3.map()
.set("foo", 1)
.set("bar", 2);
</script>
API Reference
- Objects
在JavaScript中常见的数据类型是 associative array(关联数组), 简言之就是具有一个属性集的 对象. 对象的的标准迭代方式是 for…in 循环, 但是迭代次序是未定义的.
D3
提供了几种将对象转为数字索引的标准数组的方法.请注意:当使用普通对象作为属性名是可以的,但是当使用特殊内置的属性名时会导致意想不到的事发生,比如使用
object["__proto__"] = 42
和"hasOwnProperty" in object
. 如果不能保证映射的键是安全的情况下请使用 maps 和 sets(或标准的ES6数据结构)来代替对象.d3.keys(object) <源码>
返回一个包含了指定对象属性名的数组. 数组的顺序是未定义(不可靠)的.
d3.values(object) <源码>
返回一个包含了指定对象属性值的数组. 数组的顺序是未定义(不可靠)的.
d3.entries(object) <源码>
将对象转为标准的包含
key
和value
的对象数组.也就是将对象的key-value
对重组为一个对象, 比如将{foo: 42}
转为{key: "foo", value: 42}
. 所传入的对象被重组为一个数组. 次序同样是不固定的:d3.entries({foo: 42, bar: true}); // [{key: "foo", value: 42}, {key: "bar", value: true}]
Maps
与 ES6 Maps 类似, 但是有以下几点不同:
d3.maps
的Keys
会被强制转为字符串.- 使用 map.each 遍历, 而不是 map.forEach. (并且没有 thisArg 参数.)
- 使用 map.remove, 而不是 map.delete.
- map.entries 返回 {key, value} 对象数组而不是 [key, value] 迭代器.
- map.size 是一个方法而不是 property; map.empty 同样也是方法不是属性.
d3.map([object[, key]]) <源码>
构建一个新的
map
. 如果指定了 object 则将其所有的可枚举对象复制到map
中. object 可以是一个数组也可以是其他的map
对象.可选的 key 方法用来指定使用哪个属性作为key
, 比如:var map = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; }); map.get("foo"); // {"name": "foo"} map.get("bar"); // {"name": "bar"} map.get("baz"); // undefined
参考 nests.
map.has(key) <源码>
当且仅当
map
中包含指定的 key 的时候返回true
, 要注意其对应的 value 可能为null
或者undefined
map.get(key) <源码>
返回指定的 key 对应的值,如果
map
中不包含指定的 key 则返回undefined
map.set(key, value) <源码>
设置
map
中指定的 key 为 value, 如果已经有相同的 key 字符串则会被覆盖,此方法返回map
对象因此可以链式调用. 例如:var map = d3.map() .set("foo", 1) .set("bar", 2) .set("baz", 3); map.get("foo"); // 1
map.remove(key) <源码>
如果
map
中包含指定的 key 则将其删除并返回true
, 否则什么都不做并返回false
.map.clear() <源码>
清空
map
中所有的项map.keys() <源码>
以数组的形式返回
map
中所有的 keys, 顺序是不可靠的.map.values() <源码>
以数组的形式返回
map
中所有的 value, 顺序是不可靠的.map.entries() <源码>
将
map
中所有的项重组为key-value
数组. 顺序是随意的.每一项中 key 必须是字符串, 但是对应的 value 可以是任意的类型.map.each(function) <源码>
遍历
map
中的每一项, 并对每一项执行 function, 当前项的value
和key
作为参数, 随后是map
本身, 返回undefined
.map.empty() <源码>
当且仅当
map
中没有任何项时返回true
.map.size() <源码>
返回
map
中项的个数.Sets
与 ES6 Sets类似,但是有以下不同:
- 值会被强制转换为字符串.
- set.each, 而非 set.forEach. (也没有 thisArg 参数.)
- set.remove, 而非 set.delete.
- set.size 是一个方法, 而不是 property; set.empty 也是一个方法而不是属性.
d3.set([array[, accessor]]) <源码>
构建一个新的
set
对象. 如果指定了 array 则将 array 中的元素都作为set
的元素并返回.array 也可以是其他的set
对象.如果指定了 accessor , 则在将元素添加到set
之前先对每个元素执行执行的访问器方法.set.has(value) <源码>
当
set
中包含指定的 value 字符串时返回true
.set.add(value) <源码>
将指定的 value 字符串添加到
set
中并返回set
, 因此支持链式语法:var set = d3.set() .add("foo") .add("bar") .add("baz"); set.has("foo"); // true
set.remove(value) <源码>
如果
set
中存在 value 字符串则将其移除并返回true
, 否则什么都不做并返回false
.set.clear() <源码>
清空
set
.set.values() <源码>
以数组的形式返回
set
中所有的values
, 顺序是任意不可信的,可以方便的用来去重:d3.set(["foo", "bar", "foo", "baz"]).values(); // "foo", "bar", "baz"
set.each(function) <源码>
为
set
中每个元素执行 function, 前两个参数都为value
(为了和 NestsNests
(嵌套操作)可以将数组类型的元素重组为层次结构数据。想象一下SQL
中的GROUP BY
操作。除了可以对元素进行分组之外,嵌套操作的输出是一个树而不是扁平的表。树的具体结构层级由key
函数决定。同时可以根据value
对树的叶节点进行排序,而非叶节点可以根据key
进行排序. 可选的rollup
操作可以用来指定对叶节点组进行处理的函数。Nest
操作(由nest返回的对象)是可以多次使用的,并且不保留对嵌套数据的任何引用。例如,考虑如下的扁平数据结构:
var yields = [ {yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm"}, {yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca"}, {yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris"}, ... ];
在可视化时可能需要先根据年份然后按照类别进行分组重构,可以进行如下操作:
var entries = d3.nest() .key(function(d) { return d.year; }) .key(function(d) { return d.variety; }) .entries(yields);
返回的结果是一个嵌套的数组,最外层数据都由键值对组成:
[{key: "1931", values: [ {key: "Manchuria", values: [ {yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm"}, {yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca"}, {yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris"}, ...]}, {key: "Glabron", values: [ {yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm"}, {yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca"}, ...]}, ...]}, {key: "1932", values: ...}]
嵌套的数据结构能轻松的在
SVG
或HTML
文档中生成分层结构。更多介绍参考:
- Phoebe Bright’s D3 Nest Tutorial and examples
- Shan Carter’s Mister Nester
d3.nest() <源码>
构建一个新的嵌套操作。
keys
初始为空nest.key(key) <源码>
注册一个新的
key
函数,key
函数将会在输入数组的每个元素上进行调用,并且返回一个字符串标识用来对所有元素进行分组。大多数情况下,是一个简单的访问器,就行上述例子中的年份和种类访问器一样。(key
方法并不传递当前数组的索引),每次注册key
后,其会被添加到keys
内部数组的末尾,并且嵌套操作会根据指定的key
再嵌套一层分组,某种意义上讲key
的数量决定了最终嵌套结果的深度。nest.sortKeys(comparator) <源码>
为 current key(当前 key) 指定一个 comparator 函数用以对当前
key
下的元素排序。和 d3.ascending 或 d3.descending 类似。如果没有指定,则默认的key
排序为undefined
。例如,在第一层和第二次分别根据year
和varieties
进行排序:var entries = d3.nest() .key(function(d) { return d.year; }).sortKeys(d3.ascending) .key(function(d) { return d.variety; }).sortKeys(d3.descending) .entries(yields);
排序操作仅仅影响 nest.entries 的结果。因为 nest.map 和 nest.object 返回的
keys
的顺序总是是未知的。nest.sortValues(comparator) <源码>
为嵌套操作的叶节点指定一个 comparator,和 d3.ascending 或 d3.descending 类似。这与对数组进行嵌套重组之前进行排序是大致一致的。如果没有指定比较函数,则元素的顺序会依照输入数组的顺序排序。这个操作对 nest.map, nest.entries 和 nest.object 都有影响。
nest.rollup(function) <源码>
指定一个
rollup(归纳)
函数,应用在每个分组的叶节点上。归纳函数的返回值将代替由 nest.map 或 nest.object 返回的叶节点元素。对于nest.entries 操作,它会代替用 entry.value 代替 entry.values。如果指定了 leaf comparator 则叶节会在调用归纳函数之前调用。函数参数会传递当前叶节点组。注意:上述操作都在定义嵌套的行为,要将嵌套应用到具体的数据上,需要调用nest.map
,nest.object
或者nest.entries
方法来获取嵌套后的具体形式nest.map(array) <源码>
将嵌套操作应用到指定的数组,并返回嵌套后的
[map]("https://github.com/d3/d3-collection/blob/master/src/nest.js" title="Source" target="_blank" rel="noopener noreferrer"><源码>
将指定的嵌套操作应用到指定的 array, 返回一个嵌套对象。返回的关联数组中的每一项都与第一个
key
函数相关。每一项的值取决于key
函数的个数:如果不止一个key
函数则值为一个关联数组,否则是经过key
函数过滤之后的数组。注意的是如果输入数组中包括与
JavaScript
内置key
(比如__proto__)冲突的话是不安全的。如果不能保证数组中所有的key
都是安全的请使用 nest.map 代替。nest.entries(array) <源码>
将指定的嵌套操作应用到指定的 array,返回经过嵌套处理后的
key-values
对。与将 map.entries 应用到关联数组类似。但是具有层次结构,嵌套体现在value
,如果指定了多个key
则如果对应的key
下有数据的话,其value
也为一个key-values
对象,以此类推。