web-dev-qa-db-fra.com

Rails 3. Comment faire la différence entre deux tableaux?

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>]
  • Les factures appartiennent à l'expédition.
  • L'expédition a une facture.
  • Le tableau des factures contient donc une colonne de 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.

48
leonel

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

40
pguardiario
a = [2, 4, 6, 8]
b = [1, 2, 3, 4]

a - b | b - a # => [6, 8, 1, 3]
115
Kyle Decot

Utiliser un signe de remplacement

irb(main):001:0> [1, 2, 3, 2, 6, 7] - [2, 1]
=> [3, 6, 7]
20
denis.peplin

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.

5
SRack

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
5
6ft Dan

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
5
6ft Dan

Pure Ruby est

(a + b) - (a & b)

([1,2,3,4] + [1,3]) - ([1,2,3,4] & [1,3])
=> [2,4]

a + b produira une union entre deux tableaux
Et a & b retourner l'intersection
Et union - intersection renverra la différence

2
zhisme