2011-12-26 15 views
32

Załóżmy, że mam tę tablicę z identyfikatorami wysyłek.Szyny 3. Jak uzyskać różnicę między dwiema tablicami?

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

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

Array faktur z wysyłką identyfikatory

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

[#<Invoice id: 98, shipment_id: 2>, #<Invoice id: 99, shipment_id: 3>] 
  • faktur należy do wysyłki.
  • Przesyłka ma jedną fakturę.
  • Tak więc tabela faktur zawiera kolumnę shipment_id.

Aby utworzyć fakturę, klikam Nowa faktura, a następnie dostępne jest menu wyboru z przesyłkami, dzięki czemu mogę wybrać "w której przesyłce jestem fakturą za". Dlatego chcę wyświetlić listę przesyłek, dla których nie utworzono faktury.

Potrzebuję więc szeregu przesyłek, które nie mają jeszcze faktury. W powyższym przykładzie, odpowiedź byłaby 1, 4, 5.

+1

1, 4, 5 nie jest listą identyfikatorów faktur z adresem no_shipment_id. – Robin

+0

Przepraszamy, poprawione pytanie. Dzięki za rozpatrzenie. – leonel

+2

możliwy duplikat [Znajdowanie wszystkich rekordów bez powiązanych z nimi] (http://stackoverflow.com/questions/1314408/finding-all-records-without-associated-ones) –

Odpowiedz

34

Najpierw dostaniemy listę shipping_id tych, które pojawiają się w fakturach:

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

Then „odrzuca” je z oryginalnej tablicy:

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

Uwaga: pamiętać, że odrzucanie zwraca nową tablicę, należy odrzucić! jeśli chcesz zmienić oryginalnej tablicy

+0

Jeśli używasz Rails 3.2.1+ i ActiveRecord, powinien używać pluck: 'ids = i.pluck (: id)' –

+3

To jest wykładniczo wolniejsze niż po prostu zrobienie 'xi'. Im większe tablice, tym wolniej. Oto benchmark, który napisałem porównując dwie metody: http://runnable.com/U5Y8g_nsUQokbzNl/benchmark-ruby-array-diff-methods – Ryan

+0

@Ryan - tak, ale to nie to samo. – pguardiario

20

Zastosowanie zastępczej znak

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

To nie zadziała, jeśli odwrócisz te dwie tablice. – Trip

+3

To: '[2, 1] - [1, 2, 3, 2, 6, 7]' zwraca '[]'. Czyni mnie to ciekawym, jak można odróżnić dwa dynamiczne tablice bez względu na ich kolejność. – Trip

+10

@Trip, aby odpowiedzieć na twoje pytanie, możesz zrobić coś takiego ... '(a-b) + (b-a)' gdzie otrzymasz unikalne wartości w obu tablicach, a następnie połączyć te wartości w jedną tablicę. – Ryan

5

poprzedniej odpowiedzi tutaj od pgquardiario tylko zawierał jedną różnicę kierunkową. Jeśli chcesz uzyskać różnicę z obu tablic (tak jak w przypadku, gdy oba mają unikalny element), spróbuj czegoś podobnego.

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

To najbardziej elegancka odpowiedź. 'a - b' zwraca wszystkie elementy w innym niż b. 'b - a' zwraca wszystkie elementy w b, które nie są w a | zwraca unikalny zestaw elementów z tych 2 wyników. – galatians

+0

Uwielbiam to :) Taki fajny sposób na porównanie różnic między tablicami –

+0

Świetne podejście, jednak przyzwyczajenie to do powielania wartości. Na przykład, jeśli włączone do dwóch 4, drugie 4 nie pojawi się –

4

to powinien to zrobić w jednym zapytaniu ActiveRecord

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

I wyprowadza SQL

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

W Rails 4+ można wykonać następujące po

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

I wyprowadza SQL

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

i zamiast select(:id) Polecam metodę ids.

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).ids 
Powiązane problemy