(Ruby) Comment vérifier si une gamme contient un sous-ensemble d'une autre plage?
Question
Si j'ai deux gammes qui se chevauchent:
x = 1..10
y = 5..15
Quand je dis:
puts x.include? y
la sortie est la suivante:
false
parce que les deux plages se chevauchent seulement partiellement.
Mais si je veux que ce soit « vrai » quand il y a chevauchement partiel entre les deux gammes, comment pourrais-je écrire cela? En d'autres termes je besoin d'un moyen de savoir quand une gamme contient un sous-ensemble d'une autre plage. Je suppose qu'il ya une façon élégante d'écrire ceci dans Ruby, mais les seules solutions que je peux penser sont verbeux.
La solution
Soyez prudent en utilisant ce avec de grandes plages, mais cela est une façon élégante de le faire:
(x.to_a & y.to_a).empty?
Autres conseils
La façon efficace est de comparer les limites
(x.first <= y.last) and (y.first <= x.last)
Vous pouvez également convertir les plages jeux , puisque vous faites essentiellement intersection ensemble ici. Peut-être plus facile si vous faites affaire avec plus de deux gammes.
x = (1..10).to_set
y = (5..15).to_set
!(x & y).empty? #returns true (true == overlap, false == no overlap)
Cette méthode peut être utilisée pour tester le chevauchement entre plusieurs plages de manière efficace:
def range_overlap?(ranges)
sorted_ranges = ranges.sort
sorted_ranges.each_cons(2).each do |r1, r2|
return true if r2.first <= r1.last
end
return false
end
def test(r)
puts r.inspect, range_overlap?(r)
puts '================'
r = r.reverse
puts r.inspect, range_overlap?(r)
puts '================'
end
test [[1,9], [10, 33]]
test [[1,10], [5, 8]]
test [[1,10], [10, 33]]
Si une gamme comprend soit le début ou la fin d'une deuxième gamme, alors qu'elles se chevauchent.
(x === y.first) or (x === y.last)
est la même que celle-ci:
x.include?(y.first) or x.include?(y.last)
Mais si je veux que ce soit « vrai » quand il y a chevauchement partiel entre les deux gammes, comment pourrais-je écrire cela?
Vous pouvez convertir les gammes à un tableau, et utiliser l'opérateur &
( conjonction) . Cela renvoie un nouveau tableau avec tous les éléments qui se produisent dans les deux tableaux. Si le tableau résultant est pas vide, cela signifie, qu'il ya des éléments qui se chevauchent:
def overlap?(range_1, range_2)
!(range_1.to_a & range_2.to_a).empty?
end
Si vous vérifiez de chevauchement, alors que je venais de faire
(x.include? y.first) or (x.include? y.last)
comme une gamme devra inclure au moins l'une des extrémités de l'autre. Ceci est plus intuitive pour moi que la réponse acceptée conjuction, mais pas tout à fait aussi efficace que la comparaison limite de MarkusQ.
Rails a Range # chevauche
def overlaps?(other)
cover?(other.first) || other.cover?(first)
end
Si vous utilisez Ruby 2.6, vous pouvez utiliser Range#cover?
avec un autre Range
.
(1..5).cover?(2..3) #=> true
(1..5).cover?(0..6) #=> false
(1..5).cover?(1...6) #=> true
Quelques méthodes utiles dénombrables:
# x is a 'subset' of y
x.all?{|n| y.include? n}
# x and y overlap
x.any?{|n| y.include? n}
# x and y do not overlap
x.none?{|n| y.include? n}
# x and y overlap one time
x.one?{|n| y.include? n}