Масив включає будь-яке значення з іншого масиву?


155

Який найефективніший спосіб перевірити, чи містить масив якийсь елемент із другого масиву?

Два приклади нижче, намагаючись відповісти на запитання, foodsмістить який-небудь елемент із cheeses:

cheeses = %w(chedder 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

Відповіді:


268
(cheeses & foods).empty?

Як зазначив Марк-Андре Лафортун у коментарях, &робота працює в лінійний час, тоді як any?+ include?буде квадратичним. Для великих наборів даних лінійний час буде швидшим. Для невеликих наборів даних any?+ include?може бути швидшим, як це показано у відповіді Лі Джарвіса - можливо, тому, що &виділяє новий масив, тоді як інше рішення не робить і працює як простий вкладений цикл для повернення булева.


3
Перевіряючи, чи містить масив елемент з іншого масиву, чи не було б більше сенсу робити це (сири та продукти) .any? оскільки це повертає справжнє значення, якщо масиви дійсно містять один і той же елемент?
Райан Френсіс

1
@RyanFrancis, документи: any?: Метод повертає істину , якщо блок завжди повертає значення , відмінне від помилкових або нуль. empty?: Повертає істину, якщо self не містить елементів.
Накілон

3
@Nakilon Мене також бентежить, чому відповідь - (cheeses & foods).any?це не питання ОП: якщо в сирах є якісь продукти? У його прикладі "фета" є в обох, тому результат повинен бути правдивим, правда? То навіщо перевіряти .empty?на перехресті?
SuckerForMayhem

@SuckerForMayhem, оскільки питання ОП - "Якщо такі є ... ?", А не просто "Якщо такі є?". Якщо " are ... " пропущено, передбачається, що "If it is True? " І повертає False для масиву на зразок [false, false, false], хоча він, очевидно, не порожній.
Накілон

Чи є реалізація на рівні активного запису?
Лі Чун Хой

35

Як щодо численних # будь-яких?

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

Сценарій еталону:

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

CHEESES = %w(chedder 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) } } }
end

Результат:

ruby version: 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)

Ви можете покращити це, перетворивши cheesesнабір.
акун

1
Перемістив свій власний орієнтир тут на рубінах 2.2.7 та 2.3.4 і any?, include?був найшвидшим, встановив перерву
Джаред

4
Цей орієнтир є упередженим згаданим конкретним прикладом і не обов'язково має бути більш загальним. Що робити, якщо між двома масивами не було спільних елементів? Що робити, якщо масиви були в іншому порядку на кожному проході? Що робити, якщо фета з’явилася в кінці обох масивів? Як заявив Марк-Андре, встановлене перехрестя виконується в лінійний час, тому має сенс, що він є набагато масштабнішим для загального випадку, а не для одного конкретного прикладу, що використовується виключно для з'ясування питання.
user2259664

22

Ви можете перевірити, чи перехрестя порожнє.

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

1
Set.new(cheeses).disjoint? Set.new(foods)

Також у моєму (ненауковому) орієнтирі встановлення роз'єднання було значно повільніше, ніж інші методи: gist.github.com/jaredmoody/d2a1e83de2f91fd6865920cd01a8b497
Джаред

1
Дякуємо за ваші коментарі. Я не впевнений, чому це не було Set.new, але я просто його відредагував. Я спробував ваші показники ефективності в 2.4.1. Моє зробило краще, але все ще не найкраще, використовуючи розрізнені набори, що містять більше слів. Я розміщую свою версію в коментарі до вашої суті. Я також думаю, що disjoint?це дуже елегантно, особливо в порівнянні з "будь-яким ?, включаю?". Оригінальне запитання було задано як елегантне, так і ефективне.
давидковський

.to_setМетод може бути корисний тутcheeses.to_set.disjoint?(foods.to_set)
йогоніколай

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

CHEESES = %w(chedder 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.