2011-12-26 27 views
32

Giả sử tôi có mảng này với id lô hàng.Rails 3. Làm thế nào để có được sự khác biệt giữa hai mảng?

s = Shipment.find(:all, :select => "id") 

[#<Shipment id: 1>, #<Shipment id: 2>, #<Shipment id: 3>, #<Shipment id: 4>, #<Shipment id: 5>] 

Mảng hoá đơn với

i = Invoice.find(:all, :select => "id, shipment_id") 

[#<Invoice id: 98, shipment_id: 2>, #<Invoice id: 99, shipment_id: 3>] 
  • Hoá đơn vận chuyển id của thuộc lô hàng.
  • Lô hàng có một hóa đơn.
  • Vì vậy, bảng hóa đơn có một cột là shipment_id.

Để tạo hóa đơn, tôi nhấp vào Hoá đơn mới, sau đó có menu chọn với Lô hàng, vì vậy tôi có thể chọn "lô hàng nào tôi đang tạo hóa đơn cho". Vì vậy, tôi chỉ muốn hiển thị danh sách các lô hàng mà hóa đơn chưa được tạo.

Vì vậy, tôi cần một loạt các lô hàng chưa có hóa đơn. Trong ví dụ trên, câu trả lời sẽ là 1, 4, 5.

+1

1, 4, 5 không phải là danh sách các id hóa đơn có no_shipment_id. – Robin

+0

Xin lỗi, câu hỏi đã sửa. Cảm ơn bạn đã xem xét nó. – leonel

+2

có thể trùng lặp của [Tìm tất cả hồ sơ không có liên kết] [http://stackoverflow.com/questions/1314408/finding-all-records-without-associated-ones) –

Trả lời

34

Trước tiên, bạn sẽ nhận được một danh sách của shipping_id xuất hiện trong hóa đơn:

ids = i.map{|x| x.shipment_id} 

Then 'từ chối' chúng từ mảng ban đầu của bạn:

s.reject{|x| ids.include? x.id} 

Lưu ý: hãy nhớ rằng từ chối trả về mảng mới, hãy sử dụng từ chối! nếu bạn muốn thay đổi các mảng ban đầu

+0

Nếu bạn đang sử dụng Rails 3.2.1+ và ActiveRecord bạn nên sử dụng nhổ: 'ids = i.pluck (: id)' –

+3

Đây là chậm hơn theo cấp số nhân so với chỉ làm 'xi'. Các mảng càng lớn thì càng chậm. Đây là điểm chuẩn tôi đã viết so sánh hai phương pháp http://runnable.com/U5Y8g_nsUQokbzNl/benchmark-ruby-array-diff-methods – Ryan

+0

@Ryan - vâng, nhưng đó không phải là điều tương tự. – pguardiario

20

Sử dụng thay thế dấu

irb(main):001:0> [1, 2, 3, 2, 6, 7] - [2, 1] 
=> [3, 6, 7] 
+3

Điều đó sẽ không hoạt động nếu bạn lật hai mảng xung quanh. – Trip

+3

Điều này: '[2, 1] - [1, 2, 3, 2, 6, 7]' trả về '[]'. Vì vậy, nó làm cho tôi tò mò làm thế nào bạn có thể nhận được sự khác biệt từ hai mảng động bất kể thứ tự của họ. – Trip

+10

@Trip để trả lời câu hỏi của bạn, bạn có thể làm một cái gì đó như thế này ... '(a-b) + (b-a)' nơi bạn nhận được các giá trị duy nhất trong cả hai mảng, sau đó kết hợp các giá trị đó lại với nhau thành một mảng duy nhất. – Ryan

5

Câu trả lời trước đây từ pgquardiario chỉ bao gồm một sự khác biệt định hướng. Nếu bạn muốn sự khác biệt từ cả hai mảng (như trong cả hai đều có một mục duy nhất) sau đó thử một cái gì đó như sau.

def diff(x,y) 
    o = x 
    x = x.reject{|a| if y.include?(a); a end } 
    y = y.reject{|a| if o.include?(a); a end } 
    x | y 
end 
87
a = [2, 4, 6, 8] 
b = [1, 2, 3, 4] 

a - b | b - a # => [6, 8, 1, 3] 
+12

Đây là câu trả lời thanh lịch nhất. 'a - b' trả về bất kỳ phần tử nào không ở trong b. 'b - a' trả về bất kỳ phần tử nào trong b không nằm trong a và | trả về tập hợp các phần tử duy nhất từ ​​2 kết quả đó. – galatians

+0

Tình yêu này :) Như vậy là một cách tốt đẹp để so sánh sự khác biệt mảng –

+0

Cách tiếp cận tuyệt vời, tuy nhiên, điều này sẽ không làm việc cho các giá trị trùng lặp. Ví dụ: nếu được bao gồm trong số 4 của số 4 thì số 4 sẽ không hiển thị –

4

này nên làm điều đó trong một ActiveRecord truy vấn

Shipment.where(["id NOT IN (?)", Invoice.select(:shipment_id)]).select(:id) 

Và nó ra các SQL

SELECT "shipments"."id" FROM "shipments" WHERE (id NOT IN (SELECT "invoices"."shipment_id" FROM "invoices")) 

Trong Rails 4+ bạn có thể làm theo dõi

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).select(:id) 

Và nó ra các SQL

SELECT "shipments"."id" FROM "shipments" WHERE ("shipments"."id" NOT IN (SELECT DISTINCT "invoices"."shipment_id" FROM "invoices")) 

Và thay vì select(:id) Tôi khuyên bạn nên phương pháp ids.

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).ids 
Các vấn đề liên quan