更新前後のID一覧から追加・削除・共通を調べるなら、集合の差と積が使いやすい。追加は更新後から更新前を引き、削除は更新前から更新後を引く。集合は重複数や並び順を保持するためのものではないので、表示するときにはsortedなどで順序を明示し、個数が重要な場合はCounterを使う。
比較したい単位をIDにそろえる
名簿や試料一覧を比べる場合、名前の表記だけで同一人物や同一試料を判断するより、決められたIDを使う方が分かりやすい。ここではIDの追加と削除だけを調べる。レコードの氏名や測定値が変わったかどうかは、別の比較になる。
前後の一覧をそれぞれsetへ変換すると、同じIDが複数あっても一つになる。これは存在の比較には便利だが、入力側の重複を検出する用途とは違う。名簿でIDが一意である必要があるなら、集合へ変換する前に重複を検査しておこう。
追加・削除・共通を計算する
example.py
before = ["B", "A", "A", "C"]
after = ["B", "D", "A"]
old = set(before)
new = set(after)
common = old & new
added = new - old
removed = old - new
changed = old ^ new
print("common:", sorted(common))
print("added:", sorted(added))
print("removed:", sorted(removed))
print("either side only:", sorted(changed))
assert common == {"A", "B"}
assert added == {"D"} and removed == {"C"}
assert changed == added | removed
assert new == common | added
assert old == common | removed
assert not (added & removed)
assert len(before) == 4 and len(old) == 3
assert new - new == set()
assert new - set() == new
実行結果
common: ['A', 'B']
added: ['D']
removed: ['C']
either side only: ['C', 'D']
&は両方に存在する要素、-は左側にだけ存在する要素を求める。追加と削除は引く向きが逆になるため、oldとnewの名前を明確にしておくと間違えにくい。^はどちらか一方だけに存在する要素をまとめて求める対称差である。
集合そのものの表示順に頼らない
集合をそのままprintしたときの並び順を、レポートの仕様として利用しない。例ではすべて文字列IDなのでsortedで昇順にしている。これにより、同じ入力に対して読み比べやすい差分一覧を作れる。
整数と文字列を混ぜるなど、互いに比較できない型があると、そのままsortedできない場合がある。IDの型をそろえるか、明示的なキー関数で表示順を決める。ただし、並べるための文字列変換と、同じIDとみなすための正規化は別に考える必要がある。
件数の変化は集合だけでは分からない
古い一覧にAが2回、新しい一覧にAが1回あっても、集合としてはどちらもAを一つ含むため差は出ない。出席回数や部品の必要数など、重複の個数に意味があるデータではCounterへ切り替える。
また、IDが同じままで氏名や測定値だけが変わった場合は共通集合に残る。共通IDのレコードを索引から取り出して、必要な項目を比較すれば更新内容を調べられる。追加・削除の検出と、同じIDの内容更新の検出を分けて実装すると理解しやすい。
元の順序を残したい場合
更新後の一覧に現れた順番で追加IDを表示したいなら、新しい一覧を走査し、追加集合に含まれるものを取り出す方法が使える。ただし元の一覧に重複があれば、そのままでは同じIDが複数表示される。順序を残した重複除去も併せて必要になる場合がある。
集合は検索や比較のために使い、表示用の順序は別のリストで持つと役割が明確になる。IDの意味に順序が含まれるデータ、例えば処理手順やイベント列では、集合へ変換するだけで大切な情報を落とすため注意したい。
差分レポートは確認から始める
集合差が出たからといって、その場で実データを削除する必要はない。まず追加・削除の候補として表示し、入力ファイルが正しい版か、抽出条件が前後でそろっているかを確認する。フィルター条件が変わっただけでも、大きな削除差分に見えることがある。
例では共通・追加・削除の集合が互いに混ざらないことと、新しい集合が共通と追加から再構成できることを確認している。こうした関係は、差の向きを間違えていないか確かめる助けになる。
前後が同じ、片方だけ空、完全に入れ替わる、といったケースも試しておくと、実データの大きさに惑わされず処理を確認できる。集合を使う前に「存在の比較をしたいのか」を決めることが、適切な差分処理の出発点になる。
動作確認と参考資料
掲載例はLinux・CPython 3.12.14で動作確認した。OS固有のコマンドや環境ごとに変わるパスは、本文中の条件を確認して使ってほしい。
