【D3.js in Action 3 精译_033】4.1.0 DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设

news2024/10/11 23:10:10

当前内容所在位置(可进入专栏查看其他译好的章节内容)

  • 第一部分 D3.js 基础知识
    • 第一章 D3.js 简介(已完结)
      • 1.1 何为 D3.js?
      • 1.2 D3 生态系统——入门须知
      • 1.3 数据可视化最佳实践(上)
      • 1.3 数据可视化最佳实践(下)
      • 1.4 本章小结
    • 第二章 DOM 的操作方法(已完结)
      • 2.1 第一个 D3 可视化图表
      • 2.2 环境准备
      • 2.3 用 D3 选中页面元素
      • 2.4 向选择集添加元素
      • 2.5 用 D3 设置与修改元素属性
      • 2.6 用 D3 设置与修改元素样式
      • 2.7 本章小结
    • 第三章 数据的处理(已完结)
      • 3.1 理解数据
      • 3.2 准备数据
      • 3.3 将数据绑定到 DOM 元素
        • 3.3.1 利用数据给 DOM 属性动态赋值
      • 3.4 让数据适应屏幕
        • 3.4.1 比例尺简介(上篇)
        • 3.4.2 线性比例尺(中篇)
          • 3.4.2.1 基于 Mocha 测试 D3 线性比例尺(DIY 实战)
        • 3.4.3 分段比例尺(下篇)
          • 3.4.3.1 使用 Observable 在线绘制 D3 条形图(DIY 实战)
      • 3.5 加注图表标签(上篇)
        • 3.5.1 人物专访:Krisztina Szűcs(下篇)
      • 3.6 本章小结
    • 第四章 直线、曲线与弧线的绘制 ✔️
      • 4.1 坐标轴的创建(上篇)
        • 4.1.0 DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设 ✔️
        • 4.1.1 D3 中的边距约定(精译中 ⏳)
        • 4.1.2 坐标轴的生成
      • 4.2 D3 折线图的绘制

文章目录

  • DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设
    • 1 起因
    • 2 经过
      • 2.1 日期转换中的坑
      • 2.2 关于“中规中矩”
      • 2.3 直奔源码
      • 2.4 issue 溯源
      • 2.5 踏上单元测试的漫漫征途
      • 2.6 提交 PR
    • 3 结尾

DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设

1 起因

上一篇(即本专栏第 032 篇)谈到过一个快速转换数据类型的工具函数 d3.autoType,当时作者推荐了一篇发表在 Observable 的文章,还说它是一篇写得很棒的文章(a great article)。今天就来研究研究这篇文章怎么个好法,顺便梳理一下身为码畜的本人是怎么通过这篇文章深度参与 D3.js 生态建设的(算是二度抛砖引玉吧)。

2 经过

文章链接在这里:https://observablehq.com/@d3/d3-autotype。打开一看,原来是 D3.js 的创始人 Mike Bostock 的大作!文章写于 2019 年 2 月 8 日,最后一次更新是在 2022 年 5 月 31 日(莫非是大佬送给自己的六一儿童节礼物?)。咳咳,说正题。

原来,d3.autoType 是 d3-dsv 模块下的一个工具函数,支持简单的类型推断(automatic type inference),前面先介绍它的诞生背景——让开发者从简单而繁琐的手动类型转换中解放出来,能转成数字、日期以及布尔值的就直接转了。

但问题也接踵而至:万一我要的是日期,自动转换却给我一个数字咋整?

还能咋整,特殊情况特殊处理呗,转完再手动检查一遍(这个环节不妨就叫 真·人工智能)。

2.1 日期转换中的坑

以上都是与《D3.js in Action》书中重复的内容。接下来,Mike 大佬提到了原生 JavaScript 一个奇葩的点:在解析成日期的时候,只有年月日的字符型日期(比如 "2024-10-11")会以子午本初线上的 0 时差为准;而带有时分秒这类后缀的(如 "2024-10-11T00:00"),则会默认按当地时间来转换,即结果带时差。大佬都说没办法了,这是 ECMAScript 规范写好的,强大如 Observable 也只能照办。所以就会出现这样奇葩结果:

图1 d3.autoType 在处理日期和时间戳时由于 ECMAScript 规范导致的转换不一致问题

【图1 d3.autoType 在处理日期和时间戳时由于 ECMAScript 规范导致的转换不一致问题】

其实也不难理解,一般转换个时间戳,谁会希望拿到一个和所在地区隔了 16 小时时差的结果呢?只是日期按子午线的时差来算,确实有点强人所难了。

这个坑算是原生 JavaScript 挖的,今后注意便是了。

2.2 关于“中规中矩”

文章还说,d3.autoType 还支持美国各州县的五位邮政编码转换。六位就不行?赶紧测一下:

图 2 d3.autoType 转换字符型编码没有预想中的五位数限制

【图 2 d3.autoType 转换字符型编码没有预想中的五位数限制】

那么,不是那么中规中矩的呢?它会直接跳过——

图 3 对于不那么中规中矩的数字字符串,d3.autoType 直接跳过

【图 3 对于不那么中规中矩的数字字符串,d3.autoType 直接跳过】

不仅数字如此,日期和布尔值也一样——

图 4 对于不那么中规中矩的日期和布尔值,d3.autoType 也直接跳过

【图 4 对于不那么中规中矩的日期和布尔值,d3.autoType 也直接跳过】

最后,Mike 总结了一下 d3.autoType 能转的类型:

  1. If empty, then null.(为空的,就转为 null
  2. If exactly “true”, then true.(严格写作 "true" 的,才转为 true
  3. If exactly “false”, then false.(严格写作 "false" 的,才转为 false
  4. If exactly “NaN”, then NaN.(严格写作 "NaN"的,才转为 NaN
  5. Otherwise, if coercible to a number, then a number.(以上都不行,但能强转为数字的,才转为数字)
  6. Otherwise, if a date-only or date-time string, then a Date.(以上再不行,但只有日期或者带时间的日期字符串,才转为 Date 型)
  7. Otherwise, a string (the original untrimmed value).(以上都转不了的,还转个啥,摆烂吧)

2.3 直奔源码

既然来都来了,怎么能止步于上面列出的这几条八股文呢?果断扒出 d3.autoType 的源码(根据开头提到的 d3-dsv 模块按图索骥):

// Path: d3-dsv/src/autoType.js
export default function autoType(object) {
  for (var key in object) {
    var value = object[key].trim(), number, m;
    if (!value) value = null;
    else if (value === "true") value = true;
    else if (value === "false") value = false;
    else if (value === "NaN") value = NaN;
    else if (!isNaN(number = +value)) value = number;
    else if (m = value.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)) {
      if (fixtz && !!m[4] && !m[7]) value = value.replace(/-/g, "/").replace(/T/, " ");
      value = new Date(value);
    }
    else continue;
    object[key] = value;
  }
  return object;
}

// https://github.com/d3/d3-dsv/issues/45
const fixtz = new Date("2019-01-01T00:00").getHours() || new Date("2019-07-01T00:00").getHours();

真相大白了:原来转为数字的时候,用的是 number = +value(第 9 行),即便前面写再多的 0 也是徒劳。

最长的那行正则表达式,就是处理日期的,确实麻烦了点。扔给 AI 吧,我才懒得去匹配每一个捕获组呢:

图 5 AI 对源码正则表达式的解读结果

【图 5 AI 对源码正则表达式的解读结果】

解释得还不赖。既然它这么懂源码,今后类似的活就交给它吧(有没有一种似曾相识的赶脚?)。

最后剩下那个 fixtz 常量,为啥要把 2019 年 1 月 1 号和 7 月 1 号写到一起呢?去看看上面的 issue 提案就知道了:https://github.com/d3/d3-dsv/issues/45。

这一看,便如同打开了潘多拉的魔盒……

2.4 issue 溯源

原来,这个 45 号提案已经关闭了,最后是由 53 号提案解决的。该 45 号提案还是 Mike 本人提出的:

图 6 无意间看到的作者个人简介,迷之搞笑~

【图 6 无意间看到的作者个人简介,迷之搞笑~】

他说 Safari 浏览器误将带时间的字符串默认按 UTC 子午本初线上的时差来考虑了,并且备注说这是浏览器自己的漏洞,改起来也简单(难道 Safari 是想以一己之力纠正 ECMAScript 这个历史遗留问题?)。之后有个叫 Fil 的开发者提了第 51 号提案,验证了这个说法并尝试进行修复。Fil 认为,通过分别检验冬时令和夏时令 某个日期的 00:00 零点时刻 是否真的为零,就可以复现 Safari 这个漏洞,但他不知道怎么在 node 环境下写测试用例,只在 Observable 上调通了 一个版本(可惜失效了,看不到最初的改动)。Mike 顺着这个思路对原函数进行了几处优化,经过多次沟通,最终给出了现在看到的版本。

2.5 踏上单元测试的漫漫征途

本以为事情圆满结束了,结果顺着 51 号提案的跟帖,又看到了该问题单元测试的一个修复过程。大致意思是,源码的 Bug 修复了,但是单元测试却失败了:

# (运行命令:yarn test)
# csvFormat(array) converts dates to ISO 8601
ok 131 should be equivalent
not ok 132 should be equivalent
  ---
    operator: deepEqual
    expected: |-
      'date\n2018-01-01T08:00Z'
    actual: |-
      'date\n2017-12-31T23:00Z'
    at: Test.<anonymous> (/Users/fil/Sites/d3/d3-dsv/test/csv-test.js:263:8)
    stack: |-
      Error: should be equivalent
...

根据回复中摘录的断言信息,我很快锁定了报错的测试用例(第 3 行报错):

it("csvFormat(array) converts dates to ISO 8601", () => {
  assert.deepStrictEqual(csvFormat([{date: new Date(Date.UTC(2018, 0, 1))}]), "date\n2018-01-01");
  assert.deepStrictEqual(csvFormat([{date: new Date(2018, 0, 1)}]), "date\n2018-01-01T08:00Z");
});

大佬就是大佬,Mike 一下子就看出了问题:命令行的默认时区可能和测试用例的不一致。他建议在测试脚本中硬编码一个时区,就像测试 d3-time 模块时那样:

图 7 Mike Bostock 给出的单元测试修复意见

【图 7 Mike Bostock 给出的单元测试修复意见】

要不怎么说姜还是老的辣呢,原来类似的问题早就处理过了,不用重复造轮子。在大神上帝视角般的关照下,幸运的 Fil 终于通过了测试,回复标题上都难掩激动之情:

图 8 Fil 通过测试后的回复标题(地球上任何地方都能跑通测试了)

【图 8 Fil 通过测试后的回复标题(地球上任何地方都能跑通测试了)】

有这么夸张吗?点开一看,原来是在 package.jsontest 脚本的开头加了一个 TZ=America/Los_Angeles,即 Mike 大神说的硬编码。

加了这玩意儿就真的能在我笔记本里运行?开什么玩笑?PowerShell 知道 TZ 是啥吗?带着一连串的问题,我把 d3-dsv 的代码拷到了本地:

$ git clone https://github.com/d3/d3-dsv.git
$ cd d3-dsv
$ npm install
$ npm run test

> d3-dsv@3.0.1 test
> TZ=America/Los_Angeles mocha 'test/**/*-test.js' && eslint src test

'TZ' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

怎么样?尽吹牛!还 run tests everywhere on the planet 呢,这不啪啪打脸吗?!?!

发现新大陆的惊喜是有的,但转瞬即逝:这个问题怎么解决?

既然问题是由 Safari 浏览器引发的,是否可以推断他俩的操作系统都是 Linux / Unix 这一路的?换成 Linux 就好使了?切换到 Windows 自带的 Linux 环境(即 Windows Subsystem for Linux,简称 WSL),又运行了一遍。没想到又双叒叕猜中了(之前的股票基金咋没那么神呢?):

图 9 换到 WSL 环境下运行的单元测试结果图

【图 9 换到 WSL 环境下运行的单元测试结果图】

这么一折腾,问题反而简单了:只要让 PowerShell 能运行 Linux 下的这个命令就可以了。这都不用问 AI,之前就遇到过这样的问题,加个开发依赖就搞定了:

# (under PowerShell project root)
$ npm i -D cross-env
$ (Get-Content package.json) -replace '"test": "(.*?)"', '"test": "cross-env $1"' | Set-Content package.json
$ npm run test

成败在此一举:

图 10 换回 Windows 环境下运行的单元测试结果图

【图 10 换回 Windows 环境下运行的单元测试结果图】

至此,才算真正破案了。

2.6 提交 PR

都测到这份上了,就提一个 pull request 吧。说干就干:

图 11 正式提交到 d3-dsv 官方仓库下的拉取请求(pull-request)页截图

【图 11 正式提交到 d3-dsv 官方仓库下的拉取请求(pull-request)页截图】

3 结尾

以下是此次刨根问底的几点体会:

  • 不要轻易放过《D3.js in Action》中推荐的资源链接(比如 Mike Bostock 那篇文章);
  • 边学边动手实操;
  • 尽量找到问题对应的源码;
  • 从相关的 issue 提案、pull-request 议案中厘清事情的来龙去脉;
  • 积极参与,并尝试贡献自己的开源代码。

经此一役,d3.autoType 这个知识点里的坑,我相信这辈子都不会踩第二遍了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2206177.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

又被特斯拉演了?继续“画饼式”发布Robotaxi,产业链静观其变

9月底的暴涨后&#xff0c;A股资产正经历回调&#xff0c;科技板块变现参差。不过&#xff0c;无人驾驶领域的预期依然很强。 10月10日科技股全线调整之际&#xff0c;无人驾驶板块盘中的巨幅震荡拉升就是典型的预热动作。东箭科技、天龙股份等多只智能驾驶个股涨停。核心驱动…

ACR、PZ、AMC仪表接线说明及通讯协议解析

1.ACR/PZ/AMC多功能表接线说明 三相三线接线说明 使用场合负载是平衡系统&#xff0c;并且没有零线的场合。 1. 端子号1&#xff0c;2为辅助电源&#xff1a; 如上图&#xff0c;接入相电压220V输入。其中辅助电源的火线加装5A保险丝&#xff0c;零线直接接到零排上。 2&am…

iPhone使用指南:如何在没有备份的情况下从 iPhone 恢复已删除的照片

本指南将向您展示如何在没有备份的情况下从 iPhone 恢复已删除的照片。我们所有人在生活中的某个时刻都一定做过一些愚蠢的事情&#xff0c;例如从手机或电脑中删除一些重要的东西。这是很自然的&#xff0c;没有什么可羞耻的。您可能在辛苦工作一天后回来。当突然想看一些照片…

C++开发五子棋游戏案例详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

JavaSE——集合3:ArrayList、Vector

目录 一、ArrayList的注意事项 二、ArrayList的扩容机制(重要) 三、Vector底层结构和源码剖析 1.Vector类的定义说明 2.Vector底层也是一个对象数组 3.Vector是线程同步的&#xff0c;即线程安全&#xff0c;Vector类的操作方法带有synchronized 4.在开发中&#xff0c…

obs录屏怎么样?四大优秀录屏工具亲测好用!

录屏需求日盛&#xff0c;接下来我们就来聊聊几款市面上较为热门的录屏软件——福昕录屏大师、转转大师录屏、爱拍录屏以及经典的obs录屏&#xff0c;希望能给寻找合适录屏工具的您带来一些灵感。 福昕录屏大师 直达链接&#xff1a;www.foxitsoftware.cn/REC/ 如果你刚开始…

如何在组织内推广和应用六西格玛设计?

六西格玛设计&#xff08;Design for Six Sigma, DFSS&#xff09;作为一种先进的流程设计和优化方法论&#xff0c;旨在通过设计阶段的创新与严谨&#xff0c;确保产品或服务从一开始就具备高度的质量和客户满意度。本文&#xff0c;深圳天行健企业管理咨询公司将深入探讨如何…

HCIP--以太网交换安全(三)MAC地址漂移防止与检测

MAC地址漂移防止与检测 一、MAC地址漂移防止与检测知识点 1.1MAC地址漂移的概述 MAC地址漂移是指交换机上一个vlan内有两个端口学习到同一个MAC地址&#xff0c;后学习到的MAC地址表项覆盖原MAC地址表项的现象。 1.2.MAC地址漂移的防止方法 &#xff08;1&#xff09;配置…

浸入式电磁流量计如何工作?

磁力如何产生可感应电压&#xff1f; 所有磁流量计都利用法拉第感应定律的指导原理&#xff0c;该定律显示了“表达变化的磁场在电路中感应出电压的定量关系”。 该感应定律可用于测量导体液体&#xff08;如水&#xff09;的速度&#xff0c;而无需移动部件。与其他类型的仪…

『网络游戏』游戏数据库管理类查询插入账号存储【23】

新建数据库连接 新建数据库 打开数据库 新建表 账号数据 设计表 - 添加属性 对照服务器工程GameMsg增加对应字段 保存后在服务器脚本中操作数据库数据 添加数据层文件夹 创建脚本&#xff1a;DBMgr 编写脚本&#xff1a;DBMgr.cs 修改脚本&#xff1a;ServerRoot.cs 将MySql.d…

两个数相加(c语言)

1./给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target // 的那 两个 整数&#xff0c;并返回它们的数组下标。 //你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用两次相同的元素。你可以按任意顺序返回答案。 /…

“城市酷选”排队免单模式:创新机制引领本地消费新风尚

近期&#xff0c;众多朋友对排队免单模式展现出浓厚兴趣&#xff0c;旨在借助商家优惠吸引顾客&#xff0c;激活本地商业活力&#xff0c;推动实体消费。自去年下半年起&#xff0c;本地生活服务平台热度持续攀升&#xff0c;其中&#xff0c;排队免单模式作为多商家联合的优惠…

DGX的优势

NVIDIA DGX 的 AI 领导力 文章目录 前言一、概述推动跨行业的 AI 创新二、优势客户体验到哪些好处?1. 利用生成式 AI 释放研究人员的潜力2. 加快现代应用程序的上市时间3. 利用 AI 改善客户体验三、性能性能很重要1. 为世界上最先进的超级计算机提供动力2. 打破世界纪录3. 提高…

『网络游戏』进入游戏主城UI跳转主城【26】

首先在Unity客户端中创建一个空节点重命名为MainCityWnd 设置父物体为全局 创建空节点钉在左上角作为角色信息UI 在钉子下创建Image 创建脚本&#xff1a;MainCityWnd.cs 编写脚本&#xff1a;MainCityWnd.cs 挂载脚本 创建脚本&#xff1a;MainCitySys.cs 编写脚本&#xff1a…

《Programming from the Ground Up》读后感

之所以看这本书&#xff0c;是想了解一些跟汇编相关的知识&#xff0c;打开这本书后就被作者的观点——“If you don’t understand something the first time, reread it. If you still don’t understand it, it is sometimes best to take it by faith and come back to it …

MeterSphere接口自动化平台调试

1。后置脚本节目 //导入json包 import org.json.*; import com.decode.DecodeMain; String responseprev.getResponseDataAsString(); String result DecodeMain.DecodeUtil(response); log.info(“获取批次账单id result:”result); //转换为Object对象类型 JSONObject data_…

Linux基础项目开发day2:量产工具——输入系统

文章目录 前言一、数据结构抽象1、数据本身2、数据本身3、input_manager.h 二、触摸屏编程1、touchscreen.c 三、触摸屏单元测试1、touchscreen.c2、上机测试 四、网络编程netiput.c 五、网络单元测试1、netiput.c2、client.c3、上机测试 六、输入系统的框架1、框架思路2、inpu…

5.STM32的串口通信

5.STM32的串口通信 两个串口之间的通信 发送端口&#xff1a;TX 接受端口&#xff1a;RX 注意一台设备的TX 与 另一台RX相连接 共地&#xff1a;还需要将两端地线相连接&#xff0c;将设备的参考电势在同一水平&#xff08;通讯的前提&#xff09; 建立连接 在cubeIDE中 图…

独家揭秘!新手铲屎官不可错过的宠物空气净化器选购攻略就在这

终于给我等到了双十一&#xff0c;这可是一年一度的促销力度最大的购物狂欢节&#xff0c;不要问我为什么这么期待&#xff0c;因为我养了猫&#xff0c;猫咪掉毛太严重了&#xff0c;必须得买一个宠物空气净化器&#xff0c;不然我在这个家就呆不下去了。 最近国庆节刚带猫回…

Transactional注解导致Spring Bean定时任务失效

背景 业务需要定时捞取数据库中新增的数据做数据处理及分析&#xff0c;更新状态&#xff0c;处理结束。而我们不能随意定义线程池&#xff0c;规定使用统一的标准规范来定义线程池。如在配置文件中配置线程池的属性&#xff1a;名称&#xff0c;线程核心数等&#xff0c;任务…