Tambahkan elemen ke array jika belum ada

92

Saya memiliki kelas Ruby

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 = []

Saya ingin mendorong MyClass#item1ke unique_array_of_item1, tetapi hanya jika unique_array_of_item1belum berisi itu item1. Ada solusi sederhana yang saya tahu: cukup lakukan iterasi my_arraydan periksa apakah unique_array_of_item1sudah berisi arus item1atau tidak.

Apakah ada solusi yang lebih efisien?

Alan Coromano
sumber

Jawaban:

82

Anda dapat menggunakan Set, bukan Array.

Jiří Pospíšil
sumber
Meskipun benar bahwa dokumen mengatakan bahwa Sets tidak berurutan, pada kenyataannya (seperti pada Ruby 1.9) dipesan. Jika Anda melihat kodenya, metode utama yang akan Anda gunakan untuk mendapatkan perintah (seperti Set#eachdan Set#to_a) yang didelegasikan @hash. Dan mulai dari Ruby 1.9, Hash sudah dipesan. "Hash menghitung nilainya dalam urutan saat kunci yang sesuai dimasukkan." ruby-doc.org/core-1.9.1/Hash.html
filum
Tidak pernah baru ada yang namanya satu set. Mereka luar biasa, terima kasih banyak
Brad
123

@Coorasse memiliki jawaban yang bagus , meskipun seharusnya:

my_array | [item]

Dan untuk memperbarui my_arraydi tempat:

my_array |= [item]
Jason Denney
sumber
63
atau my_array |= [item]yang akan diperbarui my_arraydi tempat
andorov
2
Mungkin saya melewatkan sesuatu di sini, tetapi operator | = sepertinya tidak bekerja untuk saya? Saya menjalankan Ruby 2.1.1
Viet
@Viet |=berfungsi dengan baik dalam pengujian saya dengan 2.1.1. Jelaskan kasus pengujian Anda atau buka pertanyaan baru.
depquid
Mengujinya lagi dan berfungsi sekarang. Tidak tahu apa yang saya lakukan sebelumnya karena komentar saya dibuat beberapa bulan yang lalu.
Viet
1
Apa kerumitannya?
Nobita
42

Anda tidak perlu mengulanginya my_arraydengan tangan.

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

Edit:

Seperti yang ditunjukkan Tombart dalam komentarnya, menggunakan Array#include?tidak terlalu efisien. Saya akan mengatakan dampak kinerja dapat diabaikan untuk Array kecil, tetapi Anda mungkin ingin memilih yang Setlebih besar.

doesterr
sumber
6
Anda pasti tidak ingin melakukan itu! array.include?(item)memiliki kompleksitas O(n)- jadi seperti mengulang seluruh larik. lihat patokan ini: gist.github.com/deric/4953652
Tombart
Solusi yang indah :). Dapat dibaca! Dalam kasus saya, saya tidak dapat memiliki satu set, jadi saya menggunakan solusi ini.
Victor
Anda juga dapat menggunakan pencarian biner untuk meningkatkan kinerja jika array dipesan. [1, 2, 3, 4, 5].bsearch { |e| e == 3 }
Victor
32

Anda dapat mengonversi item1 ke array dan menggabungkannya:

my_array | [item1]
coorasse
sumber
1
Ini seharusnya |tidak ||(lihat jawaban Jason)
Set
1
Salahku. Maaf. Diedit jawabannya
coorasse
3

Penting untuk diingat bahwa kelas Set dan | metode (juga disebut "Set Union") akan menghasilkan array elemen unik , yang sangat bagus jika Anda tidak menginginkan duplikat tetapi akan menjadi kejutan yang tidak menyenangkan jika Anda memiliki elemen non-unik dalam array asli Anda dengan desain.

Jika Anda memiliki setidaknya satu elemen duplikat dalam larik asli yang tidak ingin hilang, melakukan iterasi melalui larik dengan pengembalian awal adalah kasus terburuk O (n), yang tidak terlalu buruk dalam skema besar .

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

Saya tidak yakin apakah ini solusi yang sempurna, tetapi berhasil untuk saya:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
witkacy26
sumber