Thêm phần tử vào một mảng nếu nó chưa có


92

Tôi có một lớp 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 = []

Tôi muốn đẩy MyClass#item1đến unique_array_of_item1, nhưng chỉ khi unique_array_of_item1chưa chứa nó item1. Có một giải pháp đơn giản mà tôi biết: chỉ cần lặp lại my_arrayvà kiểm tra xem unique_array_of_item1đã chứa hiện tại item1hay chưa.

Có giải pháp nào hiệu quả hơn không?

Câu trả lời:


82

Bạn có thể sử dụng Set thay vì Array.


Mặc dù đúng là các tài liệu nói rằng Bộ không phải là thứ tự, nhưng trên thực tế, chúng được đặt hàng (kể từ Ruby 1.9). Nếu bạn nhìn vào mã, các phương pháp chính bạn sẽ sử dụng để nhận đơn đặt hàng (chẳng hạn như Set#eachSet#to_a) ủy quyền @hash. Và kể từ Ruby 1.9 Hashes được đặt hàng. "Hàm băm liệt kê các giá trị của chúng theo thứ tự các khóa tương ứng đã được chèn vào." ruby-doc.org/core-1.9.1/Hash.html
phylae

Chưa bao giờ mới có một thứ như một bộ. Họ rất tuyệt vời, cảm ơn bạn rất nhiều
Brad

123

@Coorasse có một câu trả lời tốt , mặc dù nó phải là:

my_array | [item]

Và để cập nhật my_arraytại chỗ:

my_array |= [item]

63
hoặc my_array |= [item]sẽ cập nhật my_arraytại chỗ
andorov

2
Có thể tôi đang thiếu thứ gì đó ở đây, nhưng toán tử | = dường như không hoạt động với tôi? Tôi đang chạy Ruby 2.1.1
Viet

@Viet |=hoạt động tốt trong các thử nghiệm của tôi với 2.1.1. Vui lòng mô tả trường hợp thử nghiệm của bạn hoặc mở một câu hỏi mới.
depquid

Đang kiểm tra nó một lần nữa và nó hoạt động ngay bây giờ. Không biết tôi đã làm gì sớm hơn vì nhận xét của tôi đã được đưa ra nhiều tháng trước.
Việt

1
Sự phức tạp của điều này là gì?
Nobita

40

Bạn không cần phải lặp lại my_arraybằng tay.

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

Biên tập:

Như Tombart chỉ ra trong nhận xét của mình, việc sử dụng Array#include?không hiệu quả lắm. Tôi muốn nói rằng tác động hiệu suất là không đáng kể đối với Mảng nhỏ, nhưng bạn có thể muốn đi với Mảng Setlớn hơn.


6
bạn chắc chắn không muốn làm điều đó! array.include?(item)có độ phức tạp O(n)- vì vậy nó giống như lặp lại toàn bộ mảng. hãy xem điểm chuẩn này: gist.github.com/deric/4953652
Tombart

32

Bạn có thể chuyển đổi item1 thành mảng và nối chúng:

my_array | [item1]

1
Điều này cần được |không ||(xem câu trả lời của Jason)
Seth

1
Lỗi của tôi. Lấy làm tiếc. Đã chỉnh sửa câu trả lời
coorasse

3

Điều quan trọng cần lưu ý là lớp Set và | phương thức (còn được gọi là "Set Union") sẽ mang lại một mảng các phần tử duy nhất , điều này thật tuyệt nếu bạn muốn không có bản sao nhưng sẽ là một bất ngờ khó chịu nếu bạn có các phần tử không phải là duy nhất trong mảng ban đầu của mình theo thiết kế.

Nếu bạn có ít nhất một phần tử trùng lặp trong mảng ban đầu của mình mà bạn không muốn mất, việc lặp lại qua mảng với kết quả trả về sớm là trường hợp xấu nhất O (n), điều này không quá tệ trong sơ đồ tổng thể. .

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

0

Tôi không chắc đó có phải là giải pháp hoàn hảo hay không, nhưng đã hiệu quả với tôi:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.