...または、重複エントリを防ぐ配列。
Rubyには次のようなオブジェクトがありますか?
- []、[] =、<<に応答します
- 重複するエントリをサイレントにドロップします
- 列挙可能です(または少なくともfind_allをサポートします)
- エントリが挿入された順序を保持します
私の知る限り、配列はポイント1、3、4をサポートしています。セットは1、2、3をサポートします(4はサポートしません)。そして、私のエントリは<=>を実装していないため、SortedSetは機能しません。
Ruby 1.9以降、組み込みHash
オブジェクトは挿入順序を保持します。例えば:
h = {}
h[:z] = 1
h[:b] = 2
h[:a] = 3
h[:x] = 0
p h.keys #=> [:z, :b, :a, :x]
h.delete :b
p h.keys #=> [:z, :a, :x]
h[:b] = 1
p h.keys #=> [:z, :a, :x, :b]
したがって、任意のキーに任意の値(simpleなどtrue
)を設定でき、順序セットが作成されます。いずれかを使用してキーをテストできますh.key?(obj)
。または、常に各キーを真の値に設定する場合は、を使用しますh[obj]
。キーを削除するには、を使用しますh.delete(obj)
。順序集合を配列に変換するには、を使用しますh.keys
。
現在、Ruby 1.9Set
ライブラリSet
はハッシュに基づいて構築されているため、現在、順序集合として使用できます。(たとえば、to_a
メソッドの実装は単なるもの@hash.keys
です。)ただし、この動作はそのライブラリによって保証されておらず、将来変更される可能性があることに注意してください。
require 'set'
s = Set[ :f, :o, :o, :b, :a, :r ] #=> #<Set: {:f, :o, :b, :a, :r}>
s << :z #=> #<Set: {:f, :o, :b, :a, :r, :z}>
s.delete :o #=> #<Set: {:f, :b, :a, :r, :z}>
s << :o #=> #<Set: {:f, :b, :a, :r, :z, :o}>
s << :o #=> #<Set: {:f, :b, :a, :r, :z, :o}>
s << :f #=> #<Set: {:f, :b, :a, :r, :z, :o}>
s.to_a #=> [:f, :b, :a, :r, :z, :o]
私の知る限りではありません。その数学的性質によるSetは、順序付けされていないことを意味します(または、少なくとも実装上、順序を保証しないことを意味します。実際、通常はハッシュテーブルとして実装されるため、順序が乱れます。 )。
ただし、これを行うために、配列を直接拡張するか、サブクラス化することは難しくありません。私はそれを試してみましたが、これは機能します:
class UniqueArray < Array
def initialize(*args)
if args.size == 1 and args[0].is_a? Array then
super(args[0].uniq)
else
super(*args)
end
end
def insert(i, v)
super(i, v) unless include?(v)
end
def <<(v)
super(v) unless include?(v)
end
def []=(*args)
# note: could just call super(*args) then uniq!, but this is faster
# there are three different versions of this call:
# 1. start, length, value
# 2. index, value
# 3. range, value
# We just need to get the value
v = case args.size
when 3 then args[2]
when 2 then args[1]
else nil
end
super(*args) if v.nil? or not include?(v)
end
end
すべての拠点をカバーしているようです。OReillyの便利なRubyCookbookを参照として使用しました。これらには、「ソートされた配列がソートされたままであることを確認する」ためのレシピがあります。
active_supportのOrderedHashが必要ですが、私はこのソリューションが好きです
require 'active_support/ordered_hash'
class OrderedSet < Set
def initialize enum = nil, &block
@hash = ActiveSupport::OrderedHash.new
super
end
end
=)
ハッシュを使用して値を格納し、各ハッシュペアの値に増分値を格納することができます。次に、値を介してオブジェクトにアクセスすることにより、ゆっくりではありますが、ソートされた方法でセットにアクセスできます。
さらに説明するために、後でここにいくつかのコードを追加しようとします。
値を介したアクセスは、キーによるアクセスよりもはるかに遅いことを認識しています。
更新1:Ruby 1.9では、ハッシュ要素は挿入順序で繰り返されます。
私が知っていることではありませんが、自分でロールするのは難しいことではありません。Arrayをサブクラス化し、Setを使用して一意性の制約を維持するだけです。
サイレントドロップに関する1つの質問。これは#[] =にどのように影響しますか?既存のエントリをすでに他の場所に保存されているもので上書きしようとした場合、とにかく削除される要素を削除する必要がありますか?どちらの方法でも、将来的に厄介な驚きをもたらす可能性があると思います。