几种常用的键值对类型的数据结构
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>在JavaScript中常见的数据类型是 associative array(关联数组), 简言之就是具有一个属性集的 对象. 对象的的标准迭代方式是 for…in 循环, 但是迭代次序是未定义的. D3 提供了几种将对象转为数字索引的标准数组的方法.
请注意:当使用普通对象作为属性名是可以的,但是当使用特殊内置的属性名时会导致意想不到的事发生,比如使用 object["__proto__"] = 42 和 "hasOwnProperty" in object. 如果不能保证映射的键是安全的情况下请使用 maps 和 sets(或标准的ES6数据结构)来代替对象.
返回一个包含了指定对象属性名的数组. 数组的顺序是未定义(不可靠)的.
返回一个包含了指定对象属性值的数组. 数组的顺序是未定义(不可靠)的.
将对象转为标准的包含 key 和 value 的对象数组.也就是将对象的 key-value 对重组为一个对象, 比如将 {foo: 42} 转为 {key: "foo", value: 42}. 所传入的对象被重组为一个数组. 次序同样是不固定的:
d3.entries({foo: 42, bar: true}); // [{key: "foo", value: 42}, {key: "bar", value: true}]与 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 中包含指定的 key 的时候返回 true, 要注意其对应的 value 可能为 null 或者 undefined
返回指定的 key 对应的值,如果 map 中不包含指定的 key 则返回 undefined
设置 map 中指定的 key 为 value, 如果已经有相同的 key 字符串则会被覆盖,此方法返回 map 对象因此可以链式调用. 例如:
var map = d3.map()
.set("foo", 1)
.set("bar", 2)
.set("baz", 3);
map.get("foo"); // 1如果 map 中包含指定的 key 则将其删除并返回 true, 否则什么都不做并返回 false.
清空 map 中所有的项
以数组的形式返回 map 中所有的 keys, 顺序是不可靠的.
以数组的形式返回 map 中所有的 value, 顺序是不可靠的.
将 map 中所有的项重组为 key-value 数组. 顺序是随意的.每一项中 key 必须是字符串, 但是对应的 value 可以是任意的类型.
遍历 map 中的每一项, 并对每一项执行 function, 当前项的 value 和 key 作为参数, 随后是 map 本身, 返回 undefined.
当且仅当 map 中没有任何项时返回 true.
返回 map 中项的个数.
与 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 中包含指定的 value 字符串时返回 true.
将指定的 value 字符串添加到 set 中并返回 set, 因此支持链式语法:
var set = d3.set()
.add("foo")
.add("bar")
.add("baz");
set.has("foo"); // true如果 set 中存在 value 字符串则将其移除并返回 true, 否则什么都不做并返回 false.
清空 set.
以数组的形式返回 set 中所有的 values, 顺序是任意不可信的,可以方便的用来去重:
d3.set(["foo", "bar", "foo", "baz"]).values(); // "foo", "bar", "baz"为 set 中每个元素执行 function, 前两个参数都为 value(为了和 map.each对称),其次为 set 自身. 最终返回 undefined, 遍历次序任意不可信.
当 set 中没有任何项时返回 true.
返回 set 中包含的项的个数.
Nests(嵌套操作)可以将数组类型的元素重组为层次结构数据。想象一下 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
构建一个新的嵌套操作。keys 初始为空
注册一个新的 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 都有影响。
指定一个 rollup(归纳) 函数,应用在每个分组的叶节点上。归纳函数的返回值将代替由 nest.map 或 nest.object 返回的叶节点元素。对于nest.entries 操作,它会代替用 entry.value 代替 entry.values。如果指定了 leaf comparator 则叶节点会在调用归纳函数之前调用。函数参数会传递当前叶节点组。注意:上述操作都在定义嵌套的行为,要将嵌套应用到具体的数据上,需要调用 nest.map, nest.object 或者 nest.entries 方法来获取嵌套后的具体形式
将嵌套操作应用到指定的数组,并返回嵌套后的 [map](#map)。返回的 map 映射中的每一项都对应由第一个 key 函数的返回的独特的 key 值。每一项的值都取决于注册的 key 函数的数量:如果不止一个 key 函数则值是一个 map,否则就是数组中经过过滤之后对应的 key 的元素数组。如果没有定义 key 函数,则返回输入数组。
将指定的嵌套操作应用到指定的 array, 返回一个嵌套对象。返回的关联数组中的每一项都与第一个 key 函数相关。每一项的值取决于 key 函数的个数:如果不止一个 key 函数则值为一个关联数组,否则是经过 key 函数过滤之后的数组。
注意的是如果输入数组中包括与 JavaScript 内置 key(比如__proto__)冲突的话是不安全的。如果不能保证数组中所有的 key 都是安全的请使用 nest.map 代替。
将指定的嵌套操作应用到指定的 array,返回经过嵌套处理后的 key-values 对。与将 map.entries 应用到关联数组类似。但是具有层次结构,嵌套体现在 value,如果指定了多个 key 则如果对应的 key 下有数据的话,其 value 也为一个 key-values 对象,以此类推。