2009-10-19 27 views
7

Cách nhanh nhất/một lớp lót để loại bỏ các bản sao trong một mảng các đối tượng, dựa trên một khóa cụ thể: giá trị hoặc kết quả được trả về từ một phương thức là gì?Cách nhanh nhất/Một lớp để loại bỏ các bản sao (bằng khóa) trong Ruby Array?

Ví dụ: tôi có 20 nút Phần tử XML có cùng tên nhưng chúng có các giá trị "văn bản" khác nhau, một số trong số đó là các bản sao. Tôi muốn loại bỏ các bản sao bằng cách nói "nếu element.text == previous_element.text, loại bỏ nó". Làm thế nào để tôi làm điều đó trong Ruby với số lượng mã ngắn nhất?

Tôi đã xem cách thực hiện nó cho các giá trị chuỗi/số nguyên đơn giản, nhưng không phải cho các đối tượng.

+0

Xem câu trả lời của tôi cho một hiện đại. –

Trả lời

14

Đây là cách phân tích tiêu chuẩn. Lưu ý việc sử dụng toán tử ||=, cách thuận tiện hơn (a ||= b) để viết a = b unless a.

array.inject({}) do |hash,item| 
    hash[item.text]||=item 
    hash 
end.values.inspect 

Bạn cũng có thể làm điều đó trong một dòng.

Tập lệnh cần O (n) kiểm tra bình đẳng của các chuỗi text. Đó là những gì được bảo hiểm theo O (n) khi bạn nhìn thấy một băm.

+0

Không chính xác nhanh nhất, vì nó chạy trong thời gian O (n^2). Sau đó, một lần nữa nó không thực sự quan trọng cho thời gian CPU giá rẻ như thế nào bây giờ. – EmFi

+1

@EmFi, truy cập bảng băm không dùng O (n) (chúng ta nên lặp lại chuỗi 'văn bản', nhưng chúng ta sẽ phải làm điều đó anyway). Tôi vừa mới đăng câu trả lời về vấn đề này: http://stackoverflow.com/questions/1590405/distinguishing-extra-element-from-two-arrays/1590536#1590536 –

+0

@Pavel Xin lỗi, bạn nói đúng. Tôi đã bối rối trong một giây suy nghĩ rằng các giá trị gia tăng gọi là O (n^2). Khi nó chỉ làm cho nó O (2n). – EmFi

10

này sẽ làm tất cả:

Hash[*a.map{|x| [x.text, x]}].values 

ngắn? Vâng.

(dấu sao là tùy chọn; dường như được yêu cầu cho 1.8.6).

Ví dụ:

a = [Thing.new('a'), Thing.new('b'), Thing.new('c'), Thing.new('c')] 
=> [#<Thing a>, #<Thing b>, #<Thing c>, #<Thing c>] 

Hash[a.map{|x| [x.text, x]}].values 
=> [#<Thing a>, #<Thing b>, #<Thing c>] 

Boring phần: đây là lớp học thử nghiệm nhỏ tôi đã sử dụng:

class Thing 
    attr_reader :text 
    def initialize(text) 
    @text = text 
    end 

    def inspect 
    "#<Thing #{text}>" 
    end 
end 
+0

điều này thực sự thú vị, đó là gì (&: cuối cùng)? –

+0

nó biến mất trong phiên bản mới, thậm chí ngắn hơn, đơn giản hơn :). Tuy nhiên, nói 'ary.map {| x | x.last} 'và' ary.map (&: last) 'là tương đương. – Peter

+0

Tôi có lỗi sau: trong '[] ': số lẻ đối số cho Hash (ArgumentError) –

4

Sử dụng Array#uniq với một khối. Trong trường hợp của bạn:

array.uniq(&:text) # => array with duplicated `text` removed 

này đã được giới thiệu trong Ruby 1.9.2, vì vậy nếu sử dụng phiên bản trước đó, bạn có thể sử dụng backports với require 'backports/1.9.2/array/uniq'

Các vấn đề liên quan