Zaten yoksa bir diziye öğe ekleyin


92

Ruby sınıfım var

class MyClass
  attr_writer :item1, :item2
end

my_array = get_array_of_my_class() #my_array is an array of MyClass
unique_array_of_item1 = []

Ben itmek istiyor MyClass#item1için unique_array_of_item1, ama sadece unique_array_of_item1o içermiyor item1henüz. Bildiğim basit bir çözüm var: sadece tekrarlayın my_arrayve unique_array_of_item1akımı içerip içermediğini kontrol edin item1.

Daha verimli bir çözüm var mı?

Yanıtlar:


82

Dizi yerine Set kullanabilirsiniz .


Dokümanların Setlerin sıra olmadığını söylediği doğru olsa da, aslında (Ruby 1.9'dan itibaren) sıralanmıştır. Koda bakarsanız, siparişte almak için kullanacağınız ana yöntemler ( Set#eachve gibi Set#to_a) delege @hash. Ve Ruby 1.9'dan itibaren Hash'ler sıralanır. "Kareler, değerlerini karşılık gelen anahtarların eklenme sırasına göre numaralandırır." ruby-doc.org/core-1.9.1/Hash.html
phylae

Hiç yeni olmayan bir set gibi bir şey yoktu.
Brad

123

@Coorasse bir sahip iyi bir cevap olması gerektiği olsa:

my_array | [item]

Ve my_arrayyerinde güncellemek için:

my_array |= [item]

63
veya yerinde my_array |= [item]güncellenecekmy_array
andorov

2
Belki burada bir şeyi kaçırıyorum ama | = operatörü benim için çalışmıyor gibi görünüyor? Ruby
Viet

@Viet |=, 2.1.1 ile yaptığım testlerde gayet iyi çalışıyor. Lütfen test durumunuzu tanımlayın veya yeni bir soru açın.
depquid

Tekrar test ediyorum ve şimdi çalışıyor. Aylar önce yorumum yapıldığından beri ne yapıyordum bilmiyorum.
Viet

1
Bunun karmaşıklığı nedir?
Nobita

42

my_arrayElle tekrarlamanıza gerek yok .

my_array.push(item1) unless my_array.include?(item1)

Düzenle:

Tombart'ın yorumunda belirttiği gibi kullanmak Array#include?çok verimli değil. Küçük Diziler için performans etkisinin ihmal edilebilir olduğunu söyleyebilirim, ancak Setdaha büyük diziler için gitmek isteyebilirsiniz .


6
kesinlikle bunu yapmak istemiyorsun! array.include?(item)karmaşıklığa sahiptir O(n)- bu nedenle tüm diziyi yinelemek gibidir. bu değerlendirmeye bir göz atın: gist.github.com/deric/4953652
Tombart

Güzel çözüm :). Okunabilir! Benim durumumda bir setim olamaz, bu yüzden bu çözümü kullanıyorum.
Victor

Dizi sıralanırsa performansı artırmak için ikili aramayı da kullanabilirsiniz. [1, 2, 3, 4, 5].bsearch { |e| e == 3 }
Victor

32

İtem1'i diziye dönüştürebilir ve birleştirebilirsiniz:

my_array | [item1]

1
Bu olmalı |değil ||(Jason'ın cevabını bakın)
Seth

1
Benim hatam. Afedersiniz. Cevabı
düzenledi

3

Set sınıfının ve | yöntem ("Set Union" olarak da adlandırılır) bir dizi benzersiz öğe sağlar; bu, yineleme istemiyorsanız harikadır, ancak özgün dizinizde tasarım gereği benzersiz olmayan öğeleriniz varsa, bu hoş olmayan bir sürpriz olacaktır.

Orijinal dizinizde kaybetmek istemediğiniz en az bir yinelenen öğe varsa, dizi boyunca erken dönüşle yineleme yapmak en kötü durumdur (O (n)), bu da genel şemada çok kötü değildir .

class Array
  def add_if_unique element
    return self if include? element
    push element
  end
end

0

Mükemmel bir çözüm olup olmadığından emin değilim, ama benim için çalıştı:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.