Disons que j'ai ce tableau avec les identifiants des envois.
s = Shipment.find(:all, :select => "id")
[#<Shipment id: 1>, #<Shipment id: 2>, #<Shipment id: 3>, #<Shipment id: 4>, #<Shipment id: 5>]
Tableau de factures avec identifiants d'expédition
i = Invoice.find(:all, :select => "id, shipment_id")
[#<Invoice id: 98, shipment_id: 2>, #<Invoice id: 99, shipment_id: 3>]
shipment_id
.Pour créer une facture, je clique sur Nouvelle facture, puis il y a un menu de sélection avec les envois, donc je peux choisir "pour quel envoi je crée la facture". Je souhaite donc uniquement afficher une liste des envois pour lesquels aucune facture n'a été créée.
J'ai donc besoin d'un éventail d'envois qui n'ont pas encore de facture. Dans l'exemple ci-dessus, la réponse serait 1, 4, 5.
Vous obtiendrez d'abord une liste des identifiants d'expédition qui apparaissent sur les factures:
ids = i.map{|x| x.shipment_id}
Ensuite, rejetez-les de votre tableau d'origine:
s.reject{|x| ids.include? x.id}
Remarque: rappelez-vous que le rejet renvoie un nouveau tableau, utilisez le rejet! si vous voulez changer le tableau d'origine
a = [2, 4, 6, 8]
b = [1, 2, 3, 4]
a - b | b - a # => [6, 8, 1, 3]
Utiliser un signe de remplacement
irb(main):001:0> [1, 2, 3, 2, 6, 7] - [2, 1]
=> [3, 6, 7]
Ruby 2.6 introduit Array.difference
:
[1, 1, 2, 2, 3, 3, 4, 5 ].difference([1, 2, 4]) #=> [ 3, 3, 5 ]
Donc dans le cas donné ici:
Shipment.pluck(:id).difference(Invoice.pluck(:shipment_id))
Semble une belle solution élégante au problème. Je suis un fervent adepte de a - b | b - a
, mais il peut parfois être difficile de se rappeler.
Cela règle certainement cela.
Cela devrait le faire dans une requête ActiveRecord
Shipment.where(["id NOT IN (?)", Invoice.select(:shipment_id)]).select(:id)
Et il génère le SQL
SELECT "shipments"."id" FROM "shipments" WHERE (id NOT IN (SELECT "invoices"."shipment_id" FROM "invoices"))
Dans Rails 4 + vous pouvez faire ce qui suit
Shipment.where.not(id: Invoice.select(:shipment_id).distinct).select(:id)
Et il génère le SQL
SELECT "shipments"."id" FROM "shipments" WHERE ("shipments"."id" NOT IN (SELECT DISTINCT "invoices"."shipment_id" FROM "invoices"))
Et au lieu de select(:id)
je recommande la méthode ids
.
Shipment.where.not(id: Invoice.select(:shipment_id).distinct).ids
La réponse précédente de pgquardiario ne comprenait qu'une différence unidirectionnelle. Si vous voulez la différence des deux tableaux (car ils ont tous deux un élément unique), essayez quelque chose comme ce qui suit.
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
Pure Ruby est
(a + b) - (a & b)
([1,2,3,4] + [1,3]) - ([1,2,3,4] & [1,3])
=> [2,4]
Où a + b
produira une union entre deux tableaux
Et a & b
retourner l'intersection
Et union - intersection
renverra la différence