はじめに
私ごとですが、最近RubyのプログラムをPythonに書き換える機会がありました。
その時、配列に対して集合演算する必要があり、Pythonでこの方法を調べるとlistを一度setに変換して集合演算を行い、再度listに変換し直す記事が多数でした。
setは便利ですし、大半の場合はこの方法で十分だと思いますが、わたしの場合はもとのlistの順序を保持したかったので、setを使うことはできませんでした。
よって本記事には、Pythonでsetを使わずに配列の順序を保持しながら集合演算を行う方法を残したいと思います。
目指す動作(Rubyの集合演算)
配列同士で集合演算を行い、配列を返します。
和集合「|
」
[2, 1, 1, 7, 5, 5] | [3, 3, 5, 7, 9]
# => [2, 1, 7, 5, 3, 9]
重複している要素は取り除かれます。
左辺を基準に順番が保持されています。
差集合「-
」
[2, 1, 1, 7, 5, 5] - [3, 3, 5, 7, 9]
# => [2, 1, 1]
左辺で重複していて右辺にある要素は重複している要素は取り除かれますが、
左辺で重複していて右辺にはない要素は除かれないです。
左辺を基準に順番が保持されています。
積集合「&
」
[2, 1, 1, 7, 5, 5] & [3, 3, 5, 7, 9]
# => [7, 5]
重複している要素は取り除かれます。
左辺を基準に順番が保持されています。
Pythonで実装
私が軽く探した限り、特に需要が無いからなのかPythonで配列の順序を保持しながら集合演算を行う記事は見つからなかったので自分で書いてみました。
難しいことはしていないのでコードの説明は特にしませんが、誰かの役に立てばとここに残しておこうと思います。
(2022/05/10 コメントをいただき、コードの改善を行いました)
和集合
def union_set_list(a, b):
return list(dict.fromkeys(a + b))
a = [2, 1, 1, 7, 5, 5]
b = [3, 3, 5, 7, 9]
union_set_list(a, b)
# => [2, 1, 7, 5, 3, 9]
※dict.fromkeys()で引数のシーケンスの順序が保持されることが保証されているのはPython3.7かららしいので、それより前のバージョンでは組み込み関数sorted()を使うといいと思います。
差集合
def difference_set_list(a, b):
return [i for i in a if i not in b]
a = [2, 1, 1, 7, 5, 5]
b = [3, 3, 5, 7, 9]
difference_set_list(a, b)
# => [2, 1, 1]
積集合
def intersection_set_list(a, b):
return [i for i in dict.fromkeys(a) if i in b]
a = [2, 1, 1, 7, 5, 5]
b = [3, 3, 5, 7, 9]
intersection_set_list(a, b)
# => [7, 5]