雜記 › 技術筆記 › 兩份名單裡誰只在其中一份?交集、差集、聯集怎麼算

兩份名單裡誰只在其中一份?交集、差集、聯集怎麼算

兩份名單對不上的時候,最費勁的不是比,而是「到底在比什麼」。去年公司組織健檢,行政給了我兩份從表格另存出來的 txt:一份是報名接龍的,87 行;一份是人資系統匯出的在冊名單,92 行。兩邊人數差 5,誰沒報、誰已經不在這份名單裡,靠肉眼來回找大概要花整個下午。我第一次用交集差集把這三件事分開算,幾分鐘就有了結果,但緊接著又冒出一個新問題:算出來的「差異」裡有三行其實只是多了一個空格。

先把三個詞的方向說清楚

集合運算裡最容易搞混的是差集,因為它有方向。A - B 的意思是「在 A 裡、但不在 B 裡」,把 A 和 B 換一個位置,結果就是另外一批人。我當時把報名表當作 A、在冊名單當作 B,那麼 A - B 就是「報了名但不在冊」(外部人員或者已離職),B - A 才是「在冊但沒報名」(真正要催的那批)。這兩個結果完全不同,先想清楚哪一份是基準,再決定方向,順序搞反了整件事就白做。

交集 A ∩ B 是兩邊都出現的項目,也就是「確認報了名且確實在冊」的人;聯集 A ∪ B 是把兩份合起來、相同項目只留一個,用來確認「總共涉及多少個人」。聯集是最容易被忽略卻最能提醒我這幾次資料有多亂的一個數——兩份名單各 87 和 92 行,聯集卻是 95,說明實際只有 95 個人,而兩邊加起來有 179 行。

數字對得上,才算真的算完

我做完以後會做一次減法校驗。集合運算的好處是結果之間有固定的數量關係,錯了立刻能看出來。健檢那次的四個數是自查表裡那一欄:

口徑數量怎麼核對
集合 A(報名接龍)87 行原檔案行數,含 2 行重複
集合 B(在冊名單)92 行系統匯出,無重複
交集 A ∩ B84 個兩邊都有的項目
差集 A - B3 個應等於 87 減 84
差集 B - A8 個應等於 92 減 84
聯集 A ∪ B95 個應等於 87 加 92 減 84

因為 A 裡那 2 行重複我在選項裡勾選了去重,「報了多少人」其實是 85,而 A - B 裡的 3 個是三個人名,兩邊都對得上我才往下走。這裡有個坑:如果不去重,交集會按第一次出現留一個,但你會誤以為 A 裡 87 個項目都參與了比較,催辦名單的長度就對不上。所以我習慣在結果裡同時看三個計數——A 的元素數、B 的元素數、結果的元素數,文字集合運算 會把這三個數顯示出來,夠我用來自我驗證結論。聯集還有個副作用我很在意:合併兩份來源相同的清單,同時就把重複解決了,比先合併再去重少一步。

假差異才是耗時間的地方

拿到 B - A 那 8 個以後,我發現有 3 個「明明報過名」。原因不複雜,都是文字層面的差異。

第一類是尾隨空格。表格另存成 txt 的時候,儲存格裡手滑多打一個空格很常見,「李雷 」和「李雷」在字串比較裡就是兩個東西。我的處理是先把兩邊都做了首尾空格清理再比——選項裡就有這一項。這一步會直接改變結果條數,所以我一定會記下自己有沒有開啟它:開啟了是 8 個差異,沒開啟可能變 11 個,回報時說不清是哪個就白比了。

第二類是大小寫。信箱和英文名最容易中招,LiLei@corp.com 和 lilei@corp.com 是同一個人,但如果按區分大小寫比較就會被判成兩邊各有一個。我的做法是:第一輪忽略大小寫,把結果當作「真實差異」的候選;第二輪恢復區分大小寫,看被抹平的項目有幾條,那幾條就是書寫不規範,得回頭統一而不是直接合併。

第三類是不可見字元,定位字元、全形空格,還有從別的編碼檔案貼過來時帶的行首標記,肉眼完全看不見,但會讓一行「看起來一樣」的文字被判成不同。遇到這種我一般先用文字清理類的工具把空白統一清掉,再做集合運算。

還有一類不是假差異而是真問題:重名。兩份名單裡都有「張偉」,交集會認為這是同一個人——如果工號也一併列出,那這兩行本來就不同,比對結果會多出兩條「差異」。凡是靠姓名的比對,我都會要求加上一個唯一欄,工號或者信箱都行,不然集合運算幫不上忙。

它和逐行對比不是一件事

順帶說一句容易混的:集合運算不看順序,也不看重複的位置,只問「這條在不在那一份裡」。所以它回答不了「這一版比上一版改了哪一行、第 5 行變成了什麼」。那種要保留行序、要看改動位置的比對,我用的是文字比對,兩邊按行對齊來看紅綠差別。而我如果只是想把兩份名單裡同一人重複出現的項目砍掉、按姓排好,交給文字排序去重更直接。

幾份名單能不能一起比?

工具吃的是兩個列表,所以三份以上我會分步做:先算前兩份的交集,把結果當作新的 A,再和第三份做交集;要找「任何一份裡有但別的沒有」的,就把兩兩差集分別算出來再看重合。三次的結果都得留檔,否則第二三步就說不清基準是什麼。名單再多我就不指望這種鏈式做法了,那種情況我會改成把所有名單拼成一欄、加一欄來源標記,回到表格裡做。

問得比較多的幾件事

Q:為什麼我算出來的差集比預期的多好幾條?

十之八九是格式差異,不是資料差異。先按順序排查:首尾空格清沒清、大小寫忽不忽略、有沒有定位字元或全形空格、空行是否參與。把這幾項統一之後再比一次,縮掉的那幾條就是格式造成的。

Q:A 減 B 和 B 減 A 應該看哪一個?

看你要處理誰。要「催沒做這件事的人」,就是全集減已辦集,也就是 B - A 那個方向;要「查漏、查外部資料」,就是已辦集全減。別記「左邊減右邊」,要記誰是基準。

Q:重複項目會不會把結果帶偏?

會。重複讓計數變得無法解釋,你分不清差異是「多出來的人」還是「多出來的行」。我通常在選項裡開啟去重,同時另外記一份「原始行數」和「去重後條數」,兩個數差多少本身就是資料品質的指標。

Q:聯集有什麼用,為什麼不直接兩份拼起來?

拼起來得到的是行數,聯集得到的是不重複的項目數。做統計口徑時這兩個數差別可能很大,健檢那次是 179 行對 95 條。要一份完整人員名單,用聯集;要保留每一條原始記錄,才用簡單拼接。

Q:一份名單裡有括號、後綴這類雜資訊怎麼辦?

先在兩邊做同樣的清洗,把雜資訊去掉再比,否則兩邊格式不同,交集會小得離譜。清洗動作要一致這點很重要:只洗 A 不洗 B,本來相同的人也被洗成不同的了。

回到那兩份差 5 個人的名單。最後催辦的是 8 個,其中 5 個是真沒報、3 個是空格造成的假差異——這件事給我的判斷標準就留成了兩條:一是先確認基準和方向再做差集,二是先統一文字形態(空格、大小寫、不可見字元)再算,算完用數量關係回驗一次。做到這兩條,集合運算的結果可以直接發出去;少一條,就得再花一個下午。

📎 相關文章

PNG 壓了三遍只減 426 位元組:無損壓縮的真實邊界 2026-10-22 WebP 轉 PNG 後體積漲了幾倍:什麼時候必須轉,怎麼把漲幅壓住 2026-10-22 截圖縮到四分之一,小字糊成一團的三種原因 2026-10-22 同樣裁 300×300,頭像為什麼總偏右下角半格 2026-10-22 按每月 1.7 倍預估 18 個月,腳本和表格為什麼給出兩個數 2026-10-22