まくまくRubyノート
セットを使用する(Set クラス)
2019-01-11

Ruby でセットオブジェクト(重複する値を許さないコレクション)を生成するには、標準ライブラリに含まれている Set クラスを使用します。 配列やハッシュを扱うときは、Ruby の構文として []{} を使用できましたが、セットに関しては単純なクラスとして提供されています。

Set のインスタンスを生成する

空のセットを作成する

Set オブジェクトを生成するには、コンストラクタの Set.new() を使用します。

require 'set'
s = Set.new
p s  #=> #<Set: {}>

配列からセットを作成する

Enumerable なオブジェクト、例えば配列を Set.new() に渡して Set オブジェクトを生成することできます。 この際、重複した要素は削除されます。

require 'set'
s = Set.new([3, 1, 2, 1])
p s  #=> #<Set: {3, 1, 2}>

s = Set.new(1..5)
p s  #=> #<Set: {1, 2, 3, 4, 5}>

Enumerable には to_set() メソッドが用意されているので、これを呼び出して Set オブジェクトを生成することもできます。

s = ['aaa', 'bbb', 'ccc', 'bbb'].to_set
p s  #=> #<Set: {"aaa", "bbb", "ccc"}>

任意の数のパラメータからセットを作成する

Set[] を使用すると、任意の数の値を渡して Set オブジェクトを生成することができます。 Set.new() で配列オブジェクトを渡すのと見た目は似てますが、こちらは配列オブジェクトを 1 つだけ渡しているわけではなく、複数の値を 1 ずつ引数として渡しています。

s = Set[3, 1, 2, 1]
p s  #=> #<Set: {3, 1, 2}>

Set への要素の追加と削除

要素の追加 (add, merge)

既存のセットオブジェクトに 1 つの要素を追加するには add メソッド、あるいは << 演算子を使用します。

require 'set'
s = Set.new
s.add(1)
s << 2
s.add(1)
p s  #=> #<Set: {1, 2}>

複数の要素を一度に追加したいときは、merge メソッドを使用してください(add メソッドに配列を渡してしまうと、1 つの配列要素としてセットに追加されてしまいます)。

require 'set'
s = Set[1, 2]
s.merge([2, 3])
s.merge([3, 4])
p s  #=> #<Set: {1, 2, 3, 4}>

要素の削除 (delete, subtract, delete_if)

既存のセットオブジェクトから 1 つの要素を取り除くには、delete メソッドを使用します。 存在しない要素を取り除こうとした場合は何も起こりません。

require 'set'
s = Set[1, 2, 3, 4]
s.delete(1)
s.delete(3)
s.delete(1)
p s  #=> #<Set: {2, 4}>

複数の要素をまとめて取り除くには、subtract メソッドを使用してください(delete メソッドに配列を渡してしまうと、配列をセットから取り除こうとしてしまいます)。

require 'set'
s = Set[1, 2, 3, 4]
s.subtract([1, 2])  # 1 と 2 を取り除く
s.delete([3, 4])  # [3, 4] という配列は含まれていないので何もしない
p s  #=> #<Set: {3, 4}>

Set にある値が含まれているかを調べる (include?, subset?)

Set#include? は、指定した値がその Set に含まれている場合に true を返します。

require 'set'

s = Set['aaa', 'bbb', 'ccc']
puts s.include?('aaa')  #=> true
puts s.include?('ddd')  #=> false

Set#subset?(あるいは <= 演算子)を使用すると、あるセットの要素が、もう一方のセットにすべて含まれているかどうかを調べることができます。

require 'set'
s1 = Set[1, 2, 3, 4]
s2 = Set[2, 4]
p s1 <= s2        #=> false
p s1.subset?(s2)  #=> false
p s2 <= s1        #=> true
p s2.subset?(s1)  #=> true

subset? と似たメソッドとして proper_subset? (あるいは < 演算子)がありますが、こちらは 2 つのセットの要素が完全に一致する場合に false を返すという特殊な振る舞いをします。 通常は subset? の方を使うと覚えておけばよいでしょう。

require 'set'
s1 = Set[1, 2]
s2 = Set[1, 2]
s3 = Set[1, 2, 3]
p s1 < s2  #=> false
p s1 < s3  #=> true
p s1.proper_subset?(s2)  #=> false
p s1.proper_subset?(s3)  #=> true

Set の集合演算

和集合

+ 演算子、あるいは | 演算子、あるいは union メソッドを用いることで、セット同士の和集合(いずれかのセットに含まれている要素からなる集合)を求めることができます。

require 'set'
s1 = Set[1, 2, 3, 4]
s2 = Set[3, 4, 5, 6]
p s1 + s2       #=> #<Set: {1, 2, 3, 4, 5, 6}>
p s1 | s2       #=> #<Set: {1, 2, 3, 4, 5, 6}>
p s1.union(s2)  #=> #<Set: {1, 2, 3, 4, 5, 6}>

共通集合

& 演算子を用いることで、セット同士の共通集合(両方のセットに含まれている要素からなる集合)を求めることができます。

require 'set'
s1 = Set[1, 2, 3, 4]
s2 = Set[3, 4, 5, 6]
p s1 & s2  #=> #<Set: {3, 4}>

排他的論理和 (XOR)

^ 演算子を用いることで、いずれかのセットにしか含まれていない要素を調べることができます。 つまり、要素の排他的論理和 (XOR) です。

require 'set'
s1 = Set[1, 2, 3, 4]
s2 = Set[3, 4, 5, 6]
p s1 ^ s2  #=> #<Set: {5, 6, 1, 2}>

差集合(セット1に含まれていてセット2には含まれていない要素を調べる

- 演算子、あるいは difference メソッドを用いることで、左辺に指定したセットにしか存在しない要素を調べることができます。 左辺と右辺の値を入れ替えると結果が変わってくることに注意してください。

require 'set'
s1 = Set[1, 2, 3, 4]
s2 = Set[3, 4, 5, 6]
p s1 - s2  #=> #<Set: {1, 2}>
p s2 - s1  #=> #<Set: {5, 6}>

言い換えると、「左辺のセットから右辺で指定した要素を削除したセットを作成する」とも言えます。

ちなみに、セットではなく配列でもこのような演算を行うことができます。

arr1 = [1, 2, 3, 4, 1, 2, 3, 4]
arr2 = [2, 3]
p arr1 - arr2  #=> [1, 4, 1, 4]
2019-01-11