一位支持工程师曾经问我为什么四十位客户两次收到续订电子邮件。答案花了一个小时才找到,而且完全平凡:活动列表是通过将一个导出粘贴到另一个导出下来组装的,两个导出中都存在四十个地址。没有人检查过,因为检查意味着要么盯着两千行,要么写一行 VLOOKUP 那一半的团队没有't信任。所以没有人检查,同样的四十个人两次被告知他们的卡即将被收取费用。
That is the shape of this problem 调和两个列表是任何人都对数据最常见的事情之一,而且它's足够无聊,以至于人们要么跳过它,要么做得很糟糕本能通常是伸手去拿一个diff工具,将两个列表粘贴进去,然后斜视着有色的输出- 它会立即失败,因为一个diff回答了你没有问的问题't问。或者你去电子表格并开始组装 MATCH/COUNTIF formulas,它可以工作,但需要十分钟,并生成一个工件你'll永远不会重复使用。
您实际想要的操作有一个名称,并且它'比任何工具都古老:设置算术。交叉点、差异、并集。我构建 [toolz。dev](/并放置基于浏览器的操作) 列表比较工具 在那里,但本指南是关于下面的概念 - 为什么应该忽略顺序,什么情况折叠悄然中断,以及如何在这一点和差异之间进行选择。
TL;博士: 2个列表进行比较,将每个列表视为一个无序集合,并计算交叉点(两者中的项目)、两个差异(仅A中的项目,仅B中的项目)以及每个列表内的重复项。完全忽略顺序 - line diff是错误的工具,因为它's位置,因此对列表重新排序几乎使每一行看起来都发生了变化。像电子邮件这样的标识符的折叠情况,但在输出中保留原始文本,在比较之前修剪空格,并在浏览器中执行它,因为人们调和的列表通常是客户数据。
比较两个列表实际上回答了哪些问题?
Yi旦看到命名的操作, 形状就变得明显了, 给定列表A和列表B:
- 交汇处-what's在两个?哪些订户也在付费客户上个月的哪个's SKU还在这个月's目录。
- A 减去 B-what's只在A?CRM中哪些用户从未进入计费,哪些文件存在于本地,而服务器上不存在。
- B减去A-什么's只在B?同一个问题在另一个方向,它's a 不同 题。计费中缺失和CRM中缺失是两个不同的错误,有两个不同的原因。
- 对称差-what's恰好在一个列表中?两个差异的并集:所有未能匹配的,无论方向如何这是"what's不同步?"的问题。
- 联盟- 任何一个列表中的所有内容,去重复。合并,正确完成。
- 列表中的重复项-什么's在A里面重复一个?这个根本就是't比较,但它's总是你原来需要的问题,因为它's是什么导致双发和双计费。
最后一个值得分离,跨列表匹配和列表内重复是独立的:一个地址可以在 A 中出现两次 並 B中也出现,只报告跨列表结果的工具错过了耗费金钱的失败。
这里的一切都直接映射到您已经从 SQL 了解到的操作上 - INTERSECT、 EXCEPT、 UNION- 并到电子表格公式。专用工具的价值是't它做了一些你可以做的事情't;它's所有六个答案都显示从一个粘贴,而不是六个不同的公式。
为什么差异工具是比较列表的错误选择?
这是我最常看到的错误,它'值得精确地解释,因为"比较两个列表"和"差异两个文件"听起来像同义词。
差异是位置性的。 Diff算法计算最小编辑脚本-将一个序列变成另一个序列的最短插入和删除序列。that's源代码和散文的正确模型,其中第39行后面的第40行是 有意义。移动函数和 diff 正确地报告您移动了函数。
列表没有有意义的顺序。 CRM导出中的第300行与计费导出中的第300行没有任何关系。他们're两袋物品,它们恰好按照数据库返回的任何顺序写下来。
Feed无序数据到一个位置算法, 你得到噪声。拿两个内容相同的列表, 对其中一个进行排序, 并进行差异:
List A List B
alice bob
bob alice
carol carol
一个差异报告说 alice 被移除并重新添加,或者那个 bob 动--一些搅动与两者排序的不同程度成正比正确答案是 没有改变.每一项都在两个列表中 集合相等 一个 diff 可以't 说因为是't 问会员。
的对照表,因为在寻找这两种工具的人的头脑中,这些工具确实重叠:
| 列表比较 | 文本差异 | |
|---|---|---|
| 模型 | 无序的项目集 | 有序的行序列 |
| 订购很重要? | 否 - 自由重新排序,结果相同 | 是 - 重新排序显示为更改 |
| 答案 | 会员资格:两者均仅 A、仅 B 重复 | 编辑:将 A 转换为 B 要插入/删除什么 |
| 重复项目 | 作为一个团体明确报告 | 只是更多线条 |
| 好 | 协调出口、电子邮件列表、ID、SKU、库存 | 源代码、散文、配置文件,任何位置有意义的地方 |
| 坏了 | 比较文档的两个版本 | 任何排序顺序任意的列表 |
规则: 如果您'd对列表排序不同同样满意,您需要进行集比较。 如果重新排序线路将是一个值得报告的真正变化,那么您想要 文本差异检查器。对于具有嵌套而不是平坦线条的结构化数据,两者都不适用 - that's what the JSON 迪夫 是为,因为它是按关键路径而不是按线路或按成员资格进行比较。
案例敏感性应该如何发挥作用?
这是人们默认离开然后悄悄出错的选项,所以值得思考一次的's。
Case-insensitive matching 是大多数人比较的数据的正确默认值。电子邮件地址、用户名、域名、产品代码、国家代码 - 这些在实践中是常规的case-insensitive,并且 [email protected] 並 [email protected] 在每个重要的系统中都是同一个人。
这里's这里有一个迂腐的警告,那's值得知道,因为它's偶尔承重:每 RFC 5321中,电子邮件地址的域部分是不区分大小写的,但是 当地 部分--之前的一切 @- 形式上区分大小写,并留给接收邮件服务器进行解释。所以 [email protected] 並 [email protected] 原则上可以是不同的邮箱。实际上,基本上每个主要提供商都将它们视为相同,如果您're 解复制邮件列表,您绝对应该折叠大小写。但是,如果您're 调试为什么一个特定地址会弹跳,那么's 事实证明重要的细节。
案例-敏感 case 携带信息的任何事物匹配都是正确的:Linux 文件路径、base64 字符串、哈希值、JWT 令牌、API 密钥、Git SHA、大多数编程标识符。将 case 折叠到密码哈希值列表中会合并不同的值,并自信地给出错误的答案。
比选项本身更重要的实施细节: 折叠盒进行匹配,但显示原始文本。 如果你粘贴 [email protected] 该工具告诉您它'在两个列表中,它应该交还 [email protected]- 不是 [email protected]。降低输出默默地在通过途中损坏您的数据,并且由于通常的下一步是将结果粘贴到其他地方,因此损坏会传播。该工具保留每个项目的首次看到形式,并在幕后将它们匹配到折叠的密钥上,因此得出的结果就是您放入的。
Whitespace 应该得到同样的待遇,并且得到的思考较少,从电子表格中复制一列,或者像这样分割一行 a, b, c 在逗号上,您会得到带有领先空间的物品。 [email protected] 並 [email protected] 是不同的字符串和相同的地址,由于这个原因,修剪默认打开,它's选项你'd通知在实际使用后大约三十秒内丢失。
我应该使用什么分离器?
默认情况下是每行一项,即粘贴电子表格列 - 剪贴板会移交换行分隔的值,因此来自 Excel、Google Sheets 或 CSV 导出的一列电子邮件会丢弃,而无需重新格式化。
其他分隔符涵盖已内联到达的数据。单个 CSV 行或复制数组的逗号。用于地址列表的 Outlook 和旧版 Windows 约定的分号。 shell 输出的空间 - ls、 git diff --name-only 管道穿过 tr,任何空间分隔。从电子表格水平而不是垂直粘贴的行的选项卡。
需要注意的一件事是:逗号上的分裂 不是 CSV解析,一个实数CSV字段可以包含一个引号内部的逗号,一个天真的分裂就会撕裂 "Smith, Jane" into two items。if you're 从带有引用字段的正版 CSV 文件中拉出一列,运行它通过 CSV 查看器 先- 它实现了实际的RFC 4180报价规则- 然后复制你想要的列对于没有嵌入逗号的电子邮件或ID的平面列表,拆分没问题,这不't上来。
Empty entries默认情况下会被丢弃,因为它们're几乎总是伪影:粘贴末尾的尾随换行符,电子表格中的空行,双逗号空字符串是't任何你真正关心的列表中的选项存在,如果你're专门在导出中寻找空行,这是一个真正的如果不常见的东西想要。
比较规模如何?
比较两个列表的天真方法是一个嵌套循环:对于 A 中的每个项目,扫描 B 的所有内容。That's O(n×m),它's 罚款一百个项目,无法使用五万个项目,其中您'正在进行 25 亿字符串比较。
Right approach 将每个列表索引为一个由比较键键入的哈希映射 - 项目的折叠、修剪形式 - 值是首见原来的。构建每个索引是一个线性遍历。然后每个问题都变成每个项目的常量时间查找:B's map 中的这个键吗?整个比较是 O(n+m),这意味着每边两万个项目是四万个哈希操作,完成速度比浏览器重新绘制的速度还要快。
相同的索引免费提供重复项。在构建它时计算每个密钥的出现次数;任何在 1 之上具有计数的密钥都会在该列表中重复。没有第二次通过,没有额外的结构。
实际上,上限是't比较-it's浏览器将结果组呈现为文本区域五万行。无论如何,算术都以毫秒为单位完成。如果您're例行协调那么大的列表,您可能希望在脚本而不是选项卡中进行此操作,并且上面的算法在任何语言中大约有十行。
Sorting 值得一提,结果默认自然排序,表示数字感知: item2 之前 item10而不是在它之后。简单的词典排序推杆 item10 首先是因为 1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<&l 2 符(character by character),这是通过字符串比较字母来正确的,而通过扫描ID或版本化名称时每个人类期望来错误的。关闭排序,您将获得插入顺序 - 序列中的项目它们首先出现在A中,然后是B - 这偶尔是当原始顺序编码像新近度这样的东西时您想要的。
这在实践中是什么样子?
I've实际使用这个的四种场景,每个映射到不同的结果组。
发送前清理邮件列表。 粘贴新列表和之前发送的列表。 A而已 是谁已经't被联系 - 那's你的发送列表。 在两者中 is who'd得到一个副本。 A中的重复 是本页顶部故事中的四十个人。那张支票需要十五秒,它's本来可以为支持工程师节省一个小时的那个。
协调两个系统。 CRM 将用户电子邮件导出到 A 中,并将计费导出到 B 中。 A而已 是已注册的,但从未计费; B中才有的 是从CRM中计费但缺失的,这是两个不同的错误,第一个可能是损坏的网络钩,第二个可能是有人在流程之外提出的人工发票。单个"这些列表不同"答案将完全模糊这一点,这正是为什么两个方向都单独报告的原因。
库存和目录漂移。 上个月's SKU 出口对本月's。 A而已 已停产, B中才有的 是新的, 在两者中 是结转。这里对事项进行排序- 出口以不同的订单从不同的系统中出来,一个diff会将整个文件报告为已更改。
部署合理性检查。 分期文件与生产文件,从两个 ls 输出粘贴在空格分隔符上。 A而已 是尚未't发货。
这四个方面的模式:有用的答案几乎从来都不是"列表不同。 "它's 哪 项目,在 哪 方向 - 这正是集合运算给您的以及相似性分数或差异摘要所没有的't。
我的列表是否上传到任何地方?
No,并想一想你'd粘贴到这样的工具中。
It's a 订户导出 客户邮件列表 员工ID 许可证密钥 帐号 人们对账的列表,就其性质而言,接近组织持有的最敏感数据 - 你不't 对账列表的空白,你对账列表的 人民。和"让我把这两千封客户邮件粘贴到一个随机的网站中,检查是否重叠"是一个应该让你冷淡的句子,因为在很多司法管辖区's你刚刚创建的处理器关系,没有合同。
There's没有理由让这个计算触摸到网络。it's哈希映射在字符串上-几百行无依赖的TypeScript。toolz。dev上的工具完全在你的选项卡中运行;列表是浏览器中的JavaScript字符串's内存,他们从不离开它。没有什么东西被上传,记录或存储。以你'd验证任何这样的声明的方式验证它:打开网络选项卡并点击比较,或者关闭你的wifi并观看它继续工作。I've写了更多关于为什么这个架构对于这类数据来说是重要的 为什么基于浏览器的工具击败了服务器端的工具。
常问问题
如何比较两个列表以查找它们的共同点?
A列表中粘贴一个列表,另一个粘贴到列表B中,然后按比较。"In Both"组是交叉点- 两个列表中存在的每个项目,您可以自己复制该组,下载为文本文件,或者使用复制报告一次导出每个组订单 don't 重要,因此列表 don't 需要以相同的方式排序。
如何在一个列表中找到但不在另一个列表中的项目?
The "只在A"和"只在B"组回答,他们'故意分开。只有在A中保存列表B中缺失的项目;只有在B中保存列表A中缺失的项目。这些通常是不同的问题,有不同的原因- 计费缺失和CRM缺失aren't相同的错误- 所以将它们折叠成一个答案就失去了你需要的信息。"唯一"组结合两者如果你想要对称的差异。
它可以在单个列表中找到重复项吗?
是。A中的重复和B中的重复列出了该列表中出现不止一次的每个不同的项目。这是独立于跨列表匹配的,因此一个项目既可以在A中重复,也可以在B中出现。It's通常在实践中最重要的检查,因为列表内的重复是导致重复电子邮件和双倍计费的原因。
资本化会影响比较吗?
Only if you want it。Case-sensitive matching is off by made off 默认情况下,所以 [email protected] 並 [email protected] 视为一项 - 输出保留您粘贴的表格而不是降低数据保护。在 case 承载含义的情况下打开它以获取值:Linux 路径、base64 字符串、哈希值、API 密钥、Git SHA。
What's 这与文本差异工具的区别?
A diff是位置性的:它将第1行与第1行进行比较,计算将一个序列变成另一个序列所需的编辑,因此重新排序列表几乎使每行看起来都发生了变化。该工具完全忽略顺序,只询问每边是否存在一个位置是意义的代码和散文使用diff;排序顺序是任意的,使用list comparise来协调导出。
我可以比较用逗号而不是新行分隔的列表吗?
Yes - 分隔符切换为逗号、分号、空格或 tab,默认修剪每个项目周围的空格,所以 a, b, c splits成三个干净的项目。一个警告:在逗号上拆分是't真正的csv解析,所以如果你的数据已经引用了包含逗号的字段,请先用适当的csv工具提取列。
可以处理多少个项目?
Comparison 将每个列表索引为哈希映射,并在线性时间内运行,而不是使用嵌套循环,因此每边数以万计的项目以毫秒为单位完成。实际的上限是您的浏览器将非常大的结果组渲染到页面中,而不是比较本身。
我的列表是否上传到任何地方?
No。所有的解析和比较都发生在你的浏览器中的JavaScript - 没有任何东西被传输,记录或存储。这在这里比大多数工具更重要,因为人们调和的列表通常是客户电子邮件,员工ID或许可证密钥。在比较时观察你的网络选项卡,或者离线它继续工作。
相关工具: 文本差异检查器 当秩序和立场很重要时, JSON 迪夫 为结构化数据, CSV 查看器 用于从真实 CSV 中提取色谱柱,以及 字数计数器 为快速计数。进一步阅读: 为什么基于浏览器的工具击败了服务器端的工具 並 web开发人员's工具包。



