Як перевірити, чи існує значення в масиві в Ruby


1315

У мене є значення 'Dog'і масив ['Cat', 'Dog', 'Bird'].

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


5
використовувати .include? метод . Він повертає булеве значення, яке ви хочете. У вашому випадку просто введіть: ['Cat', 'Dog', 'Bird']. Include ('Dog'), і він повинен повернути бульне значення true.
Jwan622

не використовувати включають? метод, якщо ви хочете перевірити кратні рази, щоб у масиві було присутнє різне значення чи ні, тому що включаєте? кожен раз буде повторювати масив, виконуючи операцію O (n) для пошуку кожного разу, замість цього робити хеш hash = arr.map {|x| [x,true]}.to_h, тепер перевіряйте, чи hash.has_key? 'Dog' повертається істинно чи ні
aqfaridi

3
Ви не можете реально зробити це цілком, "не перебираючи його". Логічно це неможливо, комп'ютер просто не може точно знати, чи містить масив елемент, не перебираючи елементи, щоб перевірити, чи є який-небудь з них, який він шукає. Якщо, звичайно, це не порожньо. Тоді я думаю, вам петля не потрібна.
Тім М.

Дивіться еталони нижче для тестів на різницю різних способів пошуку елемента в масиві та наборі. stackoverflow.com/a/60404934/128421
Олов'яний чоловік

Відповіді:


1945

Ви шукаєте include?:

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true

73
%w(Cat Dog Bird).include? 'Dog'
Черговий

184
Іноді мені хочеться, щоб це "містить" не включати. Я завжди змішується з включеними.
Henley Chiu

19
Дозвольте мені лише зазначити, що внутрішньо #include?все ще виконуються циклічні роботи. Однак кодер врятується від явного запису циклу. Я додав відповідь, яка виконує завдання справді без циклу.
Борис Стітнікі

8
@HenleyChiu I, яку вона називала[ 'Dog', 'Bird', 'Cat' ].has? 'Dog'

4
@AlfonsoVergara Так, будь-яке рішення для масиву повинно робити певну циклічність всередині; немає можливості перевірити приналежність масиву без циклу. Якщо ви не хочете робити циклічні роботи навіть внутрішньо, вам потрібно використовувати іншу структуру даних, наприклад ідеальну хеш-таблицю з фіксованими розмірами клавішами. Зважаючи на те, що немає можливості перевірити приналежність до масиву, не зациклившись на внутрішньому рівні, я інтерпретував питання таким чином, що "не потрібно явно писати цикл"
Брайан Кемпбелл

250

Існує in?метод у ActiveSupport(частина Rails) починаючи з v3.1, як вказував @campaterson. Тож у Rails, або якщо ви можете require 'active_support', ви можете написати:

'Unicorn'.in?(['Cat', 'Dog', 'Bird']) # => false

OTOH, у самому Ruby немає жодного inоператора чи #in?методу, навіть якщо це було запропоновано раніше, зокрема Юсуке Ендох, найвищий член рубінового ядра.

Як вказувалося іншими, зворотний метод include?існує, то для всіх Enumerableх в тому числі Array, Hash, Set, Range:

['Cat', 'Dog', 'Bird'].include?('Unicorn') # => false

Зауважте, що якщо у вас у масиві багато значень, всі вони перевірятимуться один за одним (тобто O(n)), тоді як пошук цього хешу буде постійним часом (тобто O(1)). Отже, якщо масив є постійним, наприклад, добре використовувати замість цього набір . Наприклад:

require 'set'
ALLOWED_METHODS = Set[:to_s, :to_i, :upcase, :downcase
                       # etc
                     ]

def foo(what)
  raise "Not allowed" unless ALLOWED_METHODS.include?(what.to_sym)
  bar.send(what)
end

Швидкий тест показує , що виклик include?на 10 елемента Setстановить близько 3.5x швидше , ніж виклик його еквівалент Array(якщо елемент не знайдене).

Заключна примітка: будьте обережні при використанні include?на a Range, є тонкощі, тому зверніться до документа і порівняйте з cover?...


2
Хоча Ruby не включає #in?в себе основне, якщо ви використовуєте Rails, він доступний. api.rubyonrails.org/classes/Object.html#method-i-in-3F (я знаю, що це питання Ruby, а не Rails, але це може допомогти всім, хто хоче скористатися #in?в Rails. Схоже, це було додано в Rails 3.1 apidock.com/rails/Object/in%3F
campeterson

166

Спробуйте

['Cat', 'Dog', 'Bird'].include?('Dog')

це старший синтаксис, подивіться відповідь ^^^ @ brian
jahrichie

62
@jahrichie, що саме ви вважаєте "старшим синтаксисом" у цій відповіді, необов'язкові дужки?
Денніс

3
Я згоден повинні або повинні використовувати дужки чи ні (не пов’язані зі "старими" рубіновими синтаксисами)
d1jhoni1b

Це єдиний синтаксис, який я міг би приступити до роботи в рамках потрійної операції.
Майкл Камерон

49

Використання Enumerable#include:

a = %w/Cat Dog Bird/

a.include? 'Dog'

Або, якщо зроблено ряд тестів, 1 ви можете позбутися циклу (який навіть include?є) і перейти від O (n) до O (1) за допомогою:

h = Hash[[a, a].transpose]
h['Dog']


1. Я сподіваюсь, що це очевидно, але, щоб уникнути заперечень: так, лише за декілька пошукових запитів, Hash [] і транспоніруючі ops домінують у профілі і є кожним O (n) самим собою.


48

Якщо ви хочете перевірити за допомогою блоку, ви можете спробувати any?або all?.

%w{ant bear cat}.any? {|word| word.length >= 3}   #=> true  
%w{ant bear cat}.any? {|word| word.length >= 4}   #=> true  
[ nil, true, 99 ].any?                            #=> true  

Див перечислимого для отримання додаткової інформації.

Моє натхнення прийшло від " оцінити, чи є в масиві якісь елементи в рубіні "


1
Дуже корисно, якщо ви хочете, щоб будь-який / всі ці рядки включені в інший рядок / константа
thanikkal

43

У Ruby є одинадцять методів пошуку елементів у масиві.

Кращим є include?або, для повторного доступу, створити набір, а потім зателефонувати include?або member?.

Ось усі вони:

array.include?(element) # preferred method
array.member?(element)
array.to_set.include?(element)
array.to_set.member?(element)
array.index(element) > 0
array.find_index(element) > 0
array.index { |each| each == element } > 0
array.find_index { |each| each == element } > 0
array.any? { |each| each == element }
array.find { |each| each == element } != nil
array.detect { |each| each == element } != nil

Всі вони повертають trueзначення ish, якщо елемент присутній.

include?є кращим способом. Він використовує forвнутрішньо цикл мови С, який розривається, коли елемент відповідає внутрішнім rb_equal_opt/rb_equalфункціям. Він не може бути набагато ефективнішим, якщо не створити набір для повторних перевірок членства.

VALUE
rb_ary_includes(VALUE ary, VALUE item)
{
  long i;
  VALUE e;

  for (i=0; i<RARRAY_LEN(ary); i++) {
    e = RARRAY_AREF(ary, i);
    switch (rb_equal_opt(e, item)) {
      case Qundef:
        if (rb_equal(e, item)) return Qtrue;
        break;
      case Qtrue:
        return Qtrue;
    }
  }
  return Qfalse;
}

member?не переосмислюється в Arrayкласі та використовує неоптимізовану реалізацію з Enumerableмодуля, яка буквально перераховується через усі елементи:

static VALUE
member_i(RB_BLOCK_CALL_FUNC_ARGLIST(iter, args))
{
  struct MEMO *memo = MEMO_CAST(args);

  if (rb_equal(rb_enum_values_pack(argc, argv), memo->v1)) {
    MEMO_V2_SET(memo, Qtrue);
    rb_iter_break();
  }
  return Qnil;
}

static VALUE
enum_member(VALUE obj, VALUE val)
{
  struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

  rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
  return memo->v2;
}

У перекладі на код Ruby це стосується наступного:

def member?(value)
  memo = [value, false, 0]
  each_with_object(memo) do |each, memo|
    if each == memo[0]
      memo[1] = true 
      break
    end
  memo[1]
end

Обидва include?і member?мають O (n) часову складність, оскільки обидва шукають масив для першого появи очікуваного значення.

Ми можемо використовувати набір, щоб отримати час доступу O (1) ціною, щоб спочатку створити Hash-представлення масиву. Якщо ви неодноразово перевіряєте членство в одному масиві, ця початкова інвестиція може швидко окупитися. Setне реалізовано в C, але як звичайний клас Ruby, все-таки час доступу O (1) базового @hashробить це вартим.

Ось реалізація класу Set:

module Enumerable
  def to_set(klass = Set, *args, &block)
    klass.new(self, *args, &block)
  end
end

class Set
  def initialize(enum = nil, &block) # :yields: o
    @hash ||= Hash.new
    enum.nil? and return
    if block
      do_with_enum(enum) { |o| add(block[o]) }
    else
      merge(enum)
    end
  end

  def merge(enum)
    if enum.instance_of?(self.class)
      @hash.update(enum.instance_variable_get(:@hash))
    else
      do_with_enum(enum) { |o| add(o) }
    end
    self
  end

  def add(o)
    @hash[o] = true
    self
  end

  def include?(o)
    @hash.include?(o)
  end
  alias member? include?

  ...
end

Як ви бачите, клас Set просто створює внутрішній @hashекземпляр, відображає всі об'єкти trueі потім перевіряє членство, використовуючи, Hash#include?який реалізується з часом доступу O (1) в класі Hash.

Я не буду обговорювати інші сім методів, оскільки всі вони менш ефективні.

Насправді є ще більше методів із складністю O (n) поза 11 перелічених вище, але я вирішив їх не перераховувати, оскільки вони сканують весь масив, а не розбивають у першому матчі.

Не використовуйте такі:

# bad examples
array.grep(element).any? 
array.select { |each| each == element }.size > 0
...

Як нахабно сказати, що у Рубі рівно 11 способів зробити що-небудь! Не швидше, ніж ви скажете, що хтось вкаже, що ви пропустили №12, потім №13 тощо. Щоб зробити свою думку, я запропоную інші способи, але спочатку дозвольте розпитати 11способи, які ви перерахували. По-перше, навряд чи можна вважати indexі find_index(або findі detect) окремими методами, оскільки це просто різні назви одного і того ж методу. По-друге, всі вирази, які закінчуються > 0, невірні, що, я впевнений, було недоглядом. (продовж.)
Cary Swoveland

... arr.index(e), наприклад, повертає 0if arr[0] == e. Ви згадаєте arr.index(e)повернення, nilякщо eйого немає. indexне можуть бути використані, проте, якщо один шукає nilв arr. (Та сама проблема rindex, яка не вказана.) Перетворення масиву в набір, а потім використання методів набору - це трохи розтягнення. Чому б тоді не перетворити на хеш (з ключами масиву та довільними значеннями), а потім використовувати хеш-методи? Навіть якщо перетворення на множину в порядку, є й інші методи набору, які можна використовувати, наприклад !arr.to_set.add?(e). (продовження)
Cary Swoveland

... Як і було обіцяно, ось ще кілька методів , які можуть бути використані: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]і arr & [e] == [e]. Можна було б також використовувати selectі reject.
Cary Swoveland

Я настійно рекомендую використовувати набір, якщо критерії не дозволяють дублювати копії та знати, чи існує певний елемент у списку. Встановити SOooo набагато швидше. Масиви дійсно не слід використовувати, коли потрібен пошук; Їх краще використовувати в якості черги, де тимчасово зберігаються речі для обробки. Хеш і Сет краще шукати, щоб побачити, чи щось існує.
Олов'яний чоловік

30

Кілька відповідей підказують Array#include?, але є один важливий застереження: дивлячись на джерело, навіть Array#include?виконуються циклічні роботи:

rb_ary_includes(VALUE ary, VALUE item)
{
    long i;

    for (i=0; i<RARRAY_LEN(ary); i++) {
        if (rb_equal(RARRAY_AREF(ary, i), item)) {
            return Qtrue;
        }
    }
    return Qfalse;
}

Спосіб перевірити наявність слова без циклу - побудувати трійку для свого масиву. Існує багато реалізацій трійки (google "ruby trie"). Я буду використовувати rambling-trieв цьому прикладі:

a = %w/cat dog bird/

require 'rambling-trie' # if necessary, gem install rambling-trie
trie = Rambling::Trie.create { |trie| a.each do |e| trie << e end }

І тепер ми готові перевірити наявність різних слів у вашому масиві, не перебираючи його на O(log n)час, з такою ж синтаксичною простотою Array#include?, що і за допомогою підлінійного Trie#include?:

trie.include? 'bird' #=> true
trie.include? 'duck' #=> false

8
a.each do ... endГм ... не впевнений, як це не петля
Doorknob

27
Зауважте, що це насправді включає цикл; все, що не є O (1), включає певний цикл. Це просто трапляється цикл над символами вхідного рядка. Також зверніть увагу, ніж відповідь, вже згадуваний Set#include?для людей, які турбуються про ефективність; у поєднанні з використанням символів замість рядків це може бути середній випадок O (1) (якщо ви використовуєте рядки, то просто обчислення хеша - це O (n), де n - довжина рядка). Або якщо ви хочете використовувати сторонні бібліотеки, ви можете використовувати ідеальний хеш, який є O (1) найгіршим випадком.
Брайан Кемпбелл

4
AFAIK Setвикористовує хеши для індексації своїх членів, тому насправді Set#include? має бути складним O (1) для добре розподіленого Set(точніше O (розмір введення) для хешування, а O (log (n / номер відра)) для пошуки)
Урі Агассі

11
Витрати на створення та утримання трійця так само великі. Якщо ви виконуєте багато пошукових операцій на масиві, тоді пам'ять та часові витрати на заповнення трійки та підтримання цього варті того, але для одиночних, а то й сотень чи тисяч перевірок, O (n) цілком підходить. Іншим варіантом, який не потребує додавання залежностей, буде сортування масиву або підтримка його у відсортованому порядку, і в цьому випадку для перевірки включення може використовуватися бінарний пошук O (lg n).
speakingcode

1
@speakingcode, ви можете бути з прагматичної точки зору. Але ОП просить "перевірити, чи існує значення, нічого більше, не маючи циклу". Коли я писав цю відповідь, тут було багато прагматичних рішень, але жодне, що насправді відповідало б буквальній вимозі претендента. Ваше зауваження, що BST пов'язані зі спробами, є правильним, але для рядків трие - це правильний інструмент для роботи, навіть Вікіпедія це знає дуже багато . Складність побудови та обслуговування добре реалізованого трійця надзвичайно сприятлива.
Борис Стітнікі

18

Якщо ви не хочете робити цикл, немає ніякого способу зробити це з масивами. Натомість слід використовувати набір.

require 'set'
s = Set.new
100.times{|i| s << "foo#{i}"}
s.include?("foo99")
 => true
[1,2,3,4,5,6,7,8].to_set.include?(4) 
  => true

Набори працюють як внутрішні хеші, тому Рубі не потрібно перебирати колекцію для пошуку елементів, оскільки, як випливає з назви, вона генерує хеші ключів і створює карту пам'яті, щоб кожен хеш вказував на певну точку пам'яті. Попередній приклад, зроблений за допомогою хеша:

fake_array = {}
100.times{|i| fake_array["foo#{i}"] = 1}
fake_array.has_key?("foo99")
  => true

Мінус полягає в тому, що клавіші Sets і Hash можуть включати лише унікальні елементи, і якщо ви додасте багато предметів, Ruby доведеться повторно переробити все після певної кількості елементів, щоб створити нову карту, що відповідає більшому простору клавіш. Детальніше про це я рекомендую переглянути " MountainWest RubyConf 2014 - Big O у домашньому хеші Натана Лонга ".

Ось орієнтир:

require 'benchmark'
require 'set'

array = []
set   = Set.new

10_000.times do |i|
  array << "foo#{i}"
  set   << "foo#{i}"
end

Benchmark.bm do |x|
  x.report("array") { 10_000.times { array.include?("foo9999") } }
  x.report("set  ") { 10_000.times { set.include?("foo9999")   } }
end

І результати:

      user     system      total        real
array  7.020000   0.000000   7.020000 (  7.031525)
set    0.010000   0.000000   0.010000 (  0.004816)

Якщо ви використовуєте функцію виявлення, то ви можете принаймні зменшити циклічність. Виявлення зупиниться на першому елементі "виявлено" (блок, переданий для елемента, оцінюється як істинний). Крім того, ви можете сказати виявити, що робити, якщо нічого не виявлено (ви можете перейти в лямбда).
вівторок

1
@aenw не include?зупиняється при першому попаданні?
Кіммо Лехто

ви абсолютно праві. Я настільки звик використовувати детектор, що забув про включити. дякую за ваш коментар - це гарантувало, що я оновив свої знання.
серпні

include?зупиняється на першому зверненні, але якщо це звернення знаходиться в кінці списку .... Будь-яке рішення, яке спирається на масив для зберігання, буде мати принижуючу ефективність у міру зростання списку, особливо коли потрібно знайти елемент у кінці списку список. Hash and Set не мають такої проблеми, як і упорядкований список та двійковий пошук.
Олов'яний чоловік

Це, в першу чергу, стосується цієї відповіді в першу чергу :)
Кіммо Лехто,

17

Це ще один спосіб зробити це: використовувати Array#indexметод.

Він повертає індекс першого появи елемента в масиві.

Наприклад:

a = ['cat','dog','horse']
if a.index('dog')
    puts "dog exists in the array"
end

index() також може взяти блок:

Наприклад:

a = ['cat','dog','horse']
puts a.index {|x| x.match /o/}

Це повертає індекс першого слова в масиві, який містить літеру 'o'.


indexвсе ще ітераціює над масивом, він просто повертає значення елемента.
Олов'яний чоловік

13

Смішний факт,

Ви можете використовувати *для перевірки належності масиву у caseвиразах.

case element
when *array 
  ...
else
  ...
end

Зауважте, що *в пункті "коли" це не перевіряється на наявність членства в масиві.

Застосовується вся звичайна магічна поведінка оператора splat, тому, наприклад, якщо arrayце насправді не масив, а окремий елемент, він відповідатиме цьому елементу.


Добре знати..!
Cary Swoveland

Також це буде повільна перша перевірка у справі справи, тому я використовую її в останньому whenможливому, щоб інші, більш швидкі перевірки, швидко видаляли бур'яни.
Олов'яний чоловік

9

Існує кілька способів цього досягти. Кілька з них такі:

a = [1,2,3,4,5]

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false

6
Зауважте, що Object#in?додано лише до Rails (тобто ActiveSupport) v3.1 +. Він не доступний в основному Ruby.
Том Лорд

5

Якщо вам потрібно перевірити кратні рази на наявність будь-якого ключа, перетворіть arrйого hash, а тепер перевірте O (1)

arr = ['Cat', 'Dog', 'Bird']
hash = arr.map {|x| [x,true]}.to_h
 => {"Cat"=>true, "Dog"=>true, "Bird"=>true}
hash["Dog"]
 => true
hash["Insect"]
 => false

Продуктивність Hash # has_key? проти масиву # включати?

Параметр Hash # has_key? Масив # включати

Складність часу O (1) операція O (n) операція 

Тип доступу Доступ до хеш [клавіші], якщо він взаємодіє через кожен елемент
                        повертає будь-яке значення тоді масиву до нього
                        true повертається знаходкам значення в масиві
                        Хеш # has_key? дзвінок
                        дзвінок    

Для одноразової перевірки використання include?нормально


Ви все ще не перебираєте масив, щоб перетворити його в хеш?
chrisjacob

2
так, я так, але тепер у мене є попередньо обчислена відповідь, щоб перевірити, чи існує який-небудь елемент у масиві чи ні. Тепер, коли я шукаю інше слово, знадобиться O (1) для запиту, правильно, хоча попереднє обчислення займе O (n). Насправді я використовував включати? у моєму додатку всередині циклу для перевірки того, чи існує певний елемент у масиві чи ні, він руйнує продуктивність, він перевірив у ruby-prof, це було вузьке місце
aqfaridi

1
.... але про (n) простір, а також, ні, це не час (O), тому що, як @ chris72205 справедливо вказує, ви повинні спершу пройти через свій масив. O (1) + O (n) = O (n). Так насправді це набагато гірше, ніж включати?
Рамбатіно

Чувак, я просто казав, що використання не включає цикл, для одноразової перевірки з використанням включати це добре. Тому, будь ласка, прочитайте спочатку справу використання, а краще, якщо вам потрібно перевірити кілька разів всередині циклу.
aqfaridi

Перетворення масиву в хеш коштує дорого, але використання масиву для пошуку - неправильна структура. Масиви краще, як черги, де може бути важливий порядок; Хеші та набори є кращими, коли важливі включення / існування / унікальність. Почніть з правильного контейнера, і проблема - суперечка.
Олов'яний чоловік

4

Це скаже вам не тільки про те, що воно існує, але і скільки разів воно з'являється:

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1

12
Використовувати це немає сенсу, якщо ви не хочете дізнатися, скільки разів воно з’являється, однак, як .any?повернеться, як тільки знайде перший відповідний елемент, .countзавжди буде обробляти весь масив.
Заз

Хоча це технічно скаже, чи щось існує, це також НЕ правильний спосіб зробити це, якщо ви дбаєте про швидкість.
Олов'яний чоловік

4

Для чого це варто, документи Ruby - це дивовижний ресурс для таких питань.

Я б також взяв до уваги довжину масиву, який ви шукаєте. include?Метод буде працювати лінійний пошук з O (N) складності , які можуть отримати досить потворні в залежності від розміру масиву.

Якщо ви працюєте з великим (відсортованим) масивом, я б розглядав можливість написання бінарного алгоритму пошуку, який не повинен бути надто складним і має найгірший випадок O (log n).

Або якщо ви використовуєте Ruby 2.0, ви можете скористатися цим bsearch.


3
Двійковий пошук передбачає, що масив сортується (або впорядковується в якійсь формі), що може бути дорогим для великих масивів, часто заперечуючи перевагу.
Олов'яний чоловік

Бінарний пошук також вимагає порівняння всіх пар елементів <=>, що не завжди так. Припустимо, наприклад, елементи масиву були хешами.
Cary Swoveland

1
@CarySwoveland слід більш-менш мати на увазі, що елементи в межах відсортованого масиву є порівнянними.
davissp14

4

Ви можете спробувати:

Приклад: якщо у масиві існують Cat and Dog:

(['Cat','Dog','Bird'] & ['Cat','Dog'] ).size == 2   #or replace 2 with ['Cat','Dog].size

Замість:

['Cat','Dog','Bird'].member?('Cat') and ['Cat','Dog','Bird'].include?('Dog')

Примітка: member?і include?однакові.

Це може зробити роботу в один рядок!


3

Якщо ми хочемо не використовувати include?це, також працює:

['cat','dog','horse'].select{ |x| x == 'dog' }.any?

3
будь-який? також приймає блоки: ["кішка", "собака", "кінь"]. {| х | x == 'собака'}
maikonas




2

Є навпаки.

Припустимо, масив - це [ :edit, :update, :create, :show ], можливо, цілих сім смертельних / спокійних гріхів .

І далі іграшка з ідеєю витягнути дійсну дію з якоїсь струни:

"my brother would like me to update his profile"

Тоді:

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}

1
Ваш регекс /[,|.| |]#{v.to_s}[,|.| |]/змушує мене думати, що ви хотіли знайти "ім'я дії, оточене одним із: кома, період, пробіл чи взагалі нічого", але є деякі тонкі помилки. "|update|"повернувся б [:update]і "update"повернувся б []. Класи символів ( [...]) не використовують канали ( |) для розділення символів. Навіть якщо ми змінимо їх на групи ( (...)), ви не можете зіставити порожній символ. Отже, ви, напевно, хотіли регулярного /(,|\.| |^)#{v.to_s}(,|\.| |$)/
виразу

з регулярним виразом добре (перевірено rubular.com) - і @Rambatino: чому, наприклад, це було б як Amazon Echo;) Ви можете сказати: "будь-ласка, додайте курку в мій список покупок" (і - ну - тоді вам доведеться add: додати до масиву, але я думаю, ви отримаєте суть його;)
walt_die

1
"регулярний вираз (перевірено rubular.com)". Це не нормально. Ваш регекс не буде відповідати ключовому слову на початку або в кінці рядка (наприклад, "оновити профіль мого брата"). Якщо ви не хочете збігатися з початком або кінцем, то ваш регекс все ще не в порядку, оскільки клас символів для будь-якої сторони ключового слова повинен бути/[,. ]/
bkDJ

Як говорить @bkDJ, регулярний вираз помиляється. rubular.com/r/4EG04rANz6KET6
Олов'яний чоловік

1

Якщо ви хочете повернути значення не просто істинним чи хибним, використовуйте

array.find{|x| x == 'Dog'}

Це поверне 'Собака', якщо воно є в списку, інакше буде нульовим.


Або використовувати , array.any?{|x| x == 'Dog'}якщо ви дійсно хочете , істина / неправда (НЕ значення), але і хочуть , щоб порівняти з блоком , як це.
mahemoff

0

Ось ще один спосіб зробити це:

arr = ['Cat', 'Dog', 'Bird']
e = 'Dog'

present = arr.size != (arr - [e]).size

1
Це жахливо неефективний спосіб зробити це! Я не виступаю за те, що це технічно неправильно, і хтось може дізнатися щось про Рубі, прочитавши її, але вище є багато кращих відповідей.
jibberia

Conehead, ви можете спростити arr != arr - [e]. arr & [e] == [e]інший шлях по тих же лініях.
Cary Swoveland

@CarySwoveland Не висміюй шапку майстра ;-)
Maker Wand Maker

Я мав на увазі голову майстра, а не шапку, з найбільшою повагою.
Cary Swoveland


0

у нього є багато способів знайти елемент у будь-якому масиві, але найпростіший спосіб - "in?" метод.

example:
arr = [1,2,3,4]
number = 1
puts "yes #{number} is present in arr" if number.in? arr

2
Примітка: Як описано в цій відповіді , метод in?вимагає , ActiveSupportщоб імпортувати: require active_support.
Патрік

Якщо він використовує основні розширення, він не потребує всього ActiveSupport .
Олов'яний чоловік

0

якщо ви не хочете використовувати, include?ви можете спочатку обернути елемент у масив, а потім перевірити, чи дорівнює обернутий елемент перетину масиву та загорнутого елемента. Це поверне булеве значення, засноване на рівності.

def in_array?(array, item)
    item = [item] unless item.is_a?(Array)
    item == array & item
end

-1

Мені завжди цікаво запустити деякі орієнтири, щоб побачити відносну швидкість різних способів щось робити.

Пошук елемента масиву на початку, середині чи в кінці вплине на будь-який лінійний пошук, але ледь вплине на пошук набору.

Перетворення масиву в набір призведе до звернення в процесі обробки, тому створіть набір з масиву один раз або почніть з набору з самого початку.

Ось базовий код:

# frozen_string_literal: true

require 'fruity'
require 'set'

ARRAY = (1..20_000).to_a
SET = ARRAY.to_set

DIVIDER = '-' * 20

def array_include?(elem)
  ARRAY.include?(elem)
end

def array_member?(elem)
  ARRAY.member?(elem)
end

def array_index(elem)
  ARRAY.index(elem) >= 0
end

def array_find_index(elem)
  ARRAY.find_index(elem) >= 0
end

def array_index_each(elem)
  ARRAY.index { |each| each == elem } >= 0
end

def array_find_index_each(elem)
  ARRAY.find_index { |each| each == elem } >= 0
end

def array_any_each(elem)
  ARRAY.any? { |each| each == elem }
end

def array_find_each(elem)
  ARRAY.find { |each| each == elem } != nil
end

def array_detect_each(elem)
  ARRAY.detect { |each| each == elem } != nil
end

def set_include?(elem)
  SET.include?(elem)
end

def set_member?(elem)
  SET.member?(elem)
end

puts format('Ruby v.%s', RUBY_VERSION)

{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include?        { array_include?(element)        }
    _array_member?         { array_member?(element)         }
    _array_index           { array_index(element)           }
    _array_find_index      { array_find_index(element)      }
    _array_index_each      { array_index_each(element)      }
    _array_find_index_each { array_find_index_each(element) }
    _array_any_each        { array_any_each(element)        }
    _array_find_each       { array_find_each(element)       }
    _array_detect_each     { array_detect_each(element)     }
  end
end

puts '', DIVIDER, 'Sets vs. Array.include?', DIVIDER
{
  'First' => ARRAY.first,
  'Middle' => (ARRAY.size / 2).to_i,
  'Last' => ARRAY.last
}.each do |k, element|
  puts DIVIDER, k, DIVIDER

  compare do
    _array_include? { array_include?(element) }
    _set_include?   { set_include?(element)   }
    _set_member?    { set_member?(element)    }
  end
end

Що при запуску на моєму ноутбуці Mac OS призводить до:

Ruby v.2.7.0
--------------------
First
--------------------
Running each test 65536 times. Test will take about 5 seconds.
_array_include? is similar to _array_index
_array_index is similar to _array_find_index
_array_find_index is faster than _array_any_each by 2x ± 1.0
_array_any_each is similar to _array_index_each
_array_index_each is similar to _array_find_index_each
_array_find_index_each is faster than _array_member? by 4x ± 1.0
_array_member? is faster than _array_detect_each by 2x ± 1.0
_array_detect_each is similar to _array_find_each
--------------------
Middle
--------------------
Running each test 32 times. Test will take about 2 seconds.
_array_include? is similar to _array_find_index
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 2x ± 0.1
_array_member? is faster than _array_index_each by 2x ± 0.1
_array_index_each is similar to _array_find_index_each
_array_find_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each
--------------------
Last
--------------------
Running each test 16 times. Test will take about 2 seconds.
_array_include? is faster than _array_find_index by 10.000000000000009% ± 10.0%
_array_find_index is similar to _array_index
_array_index is faster than _array_member? by 3x ± 0.1
_array_member? is faster than _array_find_index_each by 2x ± 0.1
_array_find_index_each is similar to _array_index_each
_array_index_each is similar to _array_any_each
_array_any_each is faster than _array_detect_each by 30.000000000000004% ± 10.0%
_array_detect_each is similar to _array_find_each

--------------------
Sets vs. Array.include?
--------------------
--------------------
First
--------------------
Running each test 65536 times. Test will take about 1 second.
_array_include? is similar to _set_include?
_set_include? is similar to _set_member?
--------------------
Middle
--------------------
Running each test 65536 times. Test will take about 2 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 1400x ± 1000.0
--------------------
Last
--------------------
Running each test 65536 times. Test will take about 4 minutes.
_set_member? is similar to _set_include?
_set_include? is faster than _array_include? by 3000x ± 1000.0

В основному результати підказують мені використовувати набір для всього, якщо я збираюся шукати включення, якщо я не можу гарантувати, що першим елементом є той, який я хочу, що не дуже ймовірно. Під час вставки елементів у хеш є деякі накладні витрати, але часи пошуку настільки швидкі, що я не думаю, що це колись слід враховувати. Знову ж таки, якщо вам потрібно здійснити пошук, не використовуйте масив, використовуйте набір. (Або хеш.)

Чим менше масив, тим швидше будуть працювати методи масиву, але вони все ще не збираються йти в ногу, хоча в невеликих масивах різниця може бути невеликою.

«Перший», «Середній» і «Last» відображають використання first, size / 2і lastдля ARRAYдля елемента розшукується. Цей елемент буде використовуватися при пошуку змінних ARRAYта SETзмінних.

Незначні зміни були внесені в порівнянні з методами, > 0оскільки тест повинен бути >= 0для indexтипових тестів.

Більше інформації про Фруктовість та її методологію можна знайти в її читанні .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.