169 votes

Est-ce que l'Array inclut une valeur d'un autre Array?

Quel est le moyen le plus efficace de tester si un tableau contient un élément d'un deuxième tableau ?

Deux exemples ci-dessous, tentant de répondre à la question est-ce que foods contient un élément de cheeses:

cheeses = %w(cheddar stilton brie mozzarella feta haloumi reblochon)
foods = %w(pizza feta foods bread biscuits yoghurt bacon)

puts cheeses.collect{|c| foods.include?(c)}.include?(true)

puts (cheeses - foods).size < cheeses.size

291voto

Nakilon Points 11635
(fromages & aliments).vide?

Comme l'a dit Marc-André Lafortune dans les commentaires, & fonctionne en temps linéaire tandis que any? + include? sera quadratique. Pour des ensembles de données plus importants, le temps linéaire sera plus rapide. Pour de petits ensembles de données, any? + include? peut être plus rapide comme le montre la réponse de Lee Jarvis -- probablement parce que & alloue un nouveau tableau tandis qu'une autre solution ne le fait pas et fonctionne comme une simple boucle imbriquée pour renvoyer un booléen.

3 votes

Lorsque vous vérifiez si un tableau contient un élément d'un autre tableau, ne serait-il pas plus logique de faire (cheeses & foods).any? car cela renvoie une valeur true si les tableaux contiennent effectivement l'un des mêmes éléments ?

1 votes

@RyanFrancis, docs : any? : La méthode renvoie true si le bloc renvoie une valeur autre que false ou nil. empty? : Renvoie true si self ne contient aucun élément.

3 votes

@Nakilon Je suis aussi confus pourquoi la réponse n'est pas (fromages et aliments).any? n'est-ce pas la question de l'OP : s'il y a des aliments dans les fromages? Dans son exemple, "feta" est dans les deux, donc le résultat devrait être vrai, non? Alors pourquoi vérifier .empty? sur l'intersection?

42voto

Lee Jarvis Points 7909

Que diriez-vous de Enumerable#any?

>> fromages = %w(chedder stilton brie mozzarella feta haloumi)
=> ["chedder", "stilton", "brie", "mozzarella", "feta", "haloumi"]
>> aliments = %w(pizza feta foods bread biscuits yoghurt bacon)
=> ["pizza", "feta", "foods", "bread", "biscuits", "yoghurt", "bacon"]
>> aliments.any? {|food| fromages.include?(food) }
=> true

Script de benchmark:

require "benchmark"
N = 1_000_000
puts "version de ruby: #{RUBY_VERSION}"

FROMAGES = %w(chedder stilton brie mozzarella feta haloumi).freeze
ALIMENTS = %w(pizza feta foods bread biscuits yoghurt bacon).freeze

Benchmark.bm(15) do |b|
  b.report("&, empty?") { N.times { (ALIMENTS & FROMAGES).empty? } }
  b.report("any?, include?") { N.times { ALIMENTS.any? {|food| FROMAGES.include?(food) } } }
end

Résultat:

version de ruby: 2.1.9
                      user     system      total        real
&, empty?         1.170000   0.000000   1.170000 (  1.172507)
any?, include?    0.660000   0.000000   0.660000 (  0.666015)

0 votes

Vous pouvez améliorer cela en transformant cheeses en ensemble.

1 votes

J'ai exécuté mon propre test de performance ici sur ruby 2.2.7 et 2.3.4 et any?, include? était le plus rapide, set disjoint le plus lent: gist.github.com/jaredmoody/d2a1e83de2f91fd6865920cd01a8b497

5 votes

Ce benchmark est biaisé par l'exemple spécifique mentionné et ne s'applique pas nécessairement à un cas plus général. Et s'il n'y avait aucun élément commun entre les deux tableaux ? Et si les tableaux étaient dans un ordre différent à chaque passage ? Et si le feta apparaissait à la fin des deux tableaux ? Comme Marc-André l'a indiqué, l'intersection d'ensemble s'exécute en temps linéaire, il est donc logique qu'elle soit beaucoup plus adaptable au cas général, plutôt qu'à l'exemple spécifique utilisé uniquement pour clarifier la question.

24voto

Simone Carletti Points 77653

Vous pouvez vérifier si l'intersection est vide.

cheeses = %w(chedder stilton brie mozzarella feta haloumi)
foods = %w(pizza feta foods bread biscuits yoghurt bacon)
foods & cheeses
=> ["feta"] 
(foods & cheeses).empty?
=> false

5voto

potashin Points 37585

Vous pouvez utiliser intersect? depuis ruby 3.1

foods.intersect?(cheeses)

3voto

ramonrails Points 658
require "benchmark"
N = 1_000_000
puts "ruby version: #{RUBY_VERSION}"

CHEESES = %w(cheddar stilton brie mozzarella feta haloumi).freeze
FOODS = %w(pizza feta foods bread biscuits yoghurt bacon).freeze

Benchmark.bm(15) do |b|
  b.report("&, empty?") { N.times { (FOODS & CHEESES).empty? } }  
  b.report("any?, include?") { N.times { FOODS.any? {|food| CHEESES.include?(food) } } }  
  b.report("disjoint?") { N.times { FOODS.to_set.disjoint? CHEESES.to_set }}
end  
                      user     system      total        real
&, empty?         0.751068   0.000571   0.751639 (  0.752745)
any?, include?    0.408251   0.000133   0.408384 (  0.408438)
disjoint?        11.616006   0.014806  11.630812 ( 11.637300)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X