Как проверить, существует ли значение в массиве в Ruby

1316

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

Как проверить, существует ли он в массиве, не проходя через него? Есть ли простой способ проверить, существует ли значение, не более того?

user211662
источник
5
использовать .include? метод . Возвращает логическое значение, которое вы хотите. В вашем случае просто введите: ['Cat', 'Dog', 'Bird']. Include ('Dog'), и он должен вернуть логическое значение true.
Jwan622
не использовать включить? метод, если вы хотите проверить несколько раз для различных значений, присутствующих в массиве или нет, потому что включить? каждый раз будет перебирать массив, используя операцию O (n) для поиска каждый раз. Вместо этого сделайте хеш hash = arr.map {|x| [x,true]}.to_h, теперь проверьте, hash.has_key? 'Dog' возвращает ли значение true или нет
aqfaridi
3
Вы не можете сделать это полностью "без прохождения цикла". Это логически невозможно, компьютер просто не может точно знать, содержит ли массив элемент, не просматривая элементы, чтобы проверить, является ли какой-либо из них тем, который он ищет. Если это не пусто, конечно. Тогда, я думаю, тебе не нужна петля.
Тим М.
См. Тесты ниже для тестов различия различных способов найти элемент в массиве и наборе. stackoverflow.com/a/60404934/128421
Жестянщик

Ответы:

1947

Вы ищете include?:

>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
Брайан Кэмпбелл
источник
73
Альтернативный синтаксис:%w(Cat Dog Bird).include? 'Dog'
scarver2
184
Иногда я хотел бы, чтобы это было "содержит" не включать. Я всегда путаюсь с включениями.
Хенли Чиу
19
Позвольте мне только отметить, что внутренне #include?все еще выполняет циклы. Тем не менее, кодер избавлен от явной записи цикла. Я добавил ответ, который действительно выполняет задачу без зацикливания.
Борис Ститницкий
8
@HenleyChiu Я, как это называлось[ '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?метода, хотя он был предложен ранее, в частности Юсуке Эндо, высококлассным участником ruby-core.

Как указывалось другими, обратный метод include?существует, то для всех Enumerableх в том числе Array,Hash , Set, Range:

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

Обратите внимание, что если в вашем массиве много значений, все они будут проверяться одно за другим (т.е. O(n) ), В то время как поиск хеша будет иметь постоянное время (т O(1). Е. ). Так, например, если ваш массив является константой, лучше использовать вместо него Set . Например:

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?на 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')
schmitzelburger
источник
это более старый синтаксис, посмотрите ответ ^^^ @
brian
62
@jahrichie, что именно вы считаете «старым синтаксисом» в этом ответе, необязательными скобками?
Деннис
3
я согласен @ Денис, это не старше, круглые скобки не являются обязательными, и в большинстве случаев это ХОРОШАЯ ПРАКТИКА .... попробуйте использовать без скобок в одной строке, если предложение, например, я имел в виду, что в зависимости от вашего случая вы должны или должны использовать скобки или нет (вообще не связанные со «старым» синтаксисом ruby)
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 [] и транспонирования доминируют в профиле и каждый из них O (n) .

DigitalRoss
источник
49

Если вы хотите проверить блок, вы можете попробовать 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  

Смотри Enumerable для получения дополнительной информации.

Мое вдохновение пришло от " оценить, есть ли в массиве какие-либо элементы в ruby "

фургон
источник
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) временную сложность, поскольку оба ищут в массиве первое вхождение ожидаемого значения.

Мы можем использовать Set, чтобы получить O (1) время доступа за счет необходимости сначала создать хеш-представление массива. Если вы неоднократно проверяете членство в одном и том же массиве, первоначальные инвестиции могут быстро окупиться. 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
...
akuhn
источник
Как нагло говорить, что у Руби ровно 11 способов что-либо сделать! Как только вы скажете, что кто-то укажет, что вы пропустили № 12, затем № 13 и так далее. Чтобы высказать свою точку зрения, я предложу другие пути, но сначала позвольте мне поставить под сомнение 11пути, которые вы перечислили. Во-первых, вы вряд ли можете считать indexи find_index(или findи detect) как отдельные методы, так как это просто разные имена для одного и того же метода. Во-вторых, все выражения, которые заканчиваются > 0, неверны, что, я уверен, было упущением. (продолжение)
Кэри Свовеланд,
... arr.index(e)например, возвращает 0if arr[0] == e. Вы вспомните arr.index(e)возвраты, nilесли eнет. indexне может использоваться, однако, если кто-то ищет nilв arr. (Та же самая проблема rindex, которой нет в списке.) Преобразование массива в набор, а затем использование методов набора немного растягивается. Почему бы тогда не преобразовать в хеш (с ключами из массива и произвольными значениями), а затем использовать методы хеша? Даже если преобразование в набор в порядке, существуют другие методы набора, которые можно использовать, например !arr.to_set.add?(e). (продолжение)
Кэри Свовеланд,
... Как и было обещано, вот еще несколько методов , которые могут быть использованы: arr.count(e) > 0, arr != arr.dup.delete(e) , arr != arr - [e]и arr & [e] == [e]. Можно также использовать selectи reject.
Кэри Свовеланд,
Я настоятельно рекомендую использовать Set, если критерии таковы, что дубликаты не допускаются, и знать, существует ли определенный элемент в списке. Установить так намного быстрее. Массивы действительно не должны использоваться, когда необходим поиск; Их лучше использовать как очередь, где вещи временно хранятся для обработки по порядку. Хэш и Сет лучше искать, если что-то существует.
Жестянщик
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;
}

Чтобы проверить наличие слова без зацикливания, нужно создать дерево для вашего массива. Существует множество реализаций trie (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Хм ... не уверен, что это не петля
tckmn
27
Обратите внимание, что это действительно включает цикл; все, что не O (1), включает в себя какой-то цикл. Это просто цикл над символами входной строки. Также обратите внимание, что ответ уже упоминался Set#include?для людей, которые обеспокоены эффективностью; в сочетании с использованием символов вместо строк, это может быть средний случай O (1) (если вы используете строки, тогда просто вычисление хэша - это O (n), где n - длина строки). Или, если вы хотите использовать сторонние библиотеки, вы можете использовать идеальный хеш, который является O (1) худшим случаем.
Брайан Кэмпбелл
4
AFAIK, Setиспользует хэши для индексации своих членов, поэтому фактически Set#include? должен иметь сложность O (1) для хорошо распределенного Set(точнее O (input-size) для хеширования и O (log (n / bucket-number)) для в поисках)
Ури Агасси
11
Затраты на создание и поддержание Trie также высоки. Если вы выполняете много операций поиска в массиве, то затраты памяти и времени на заполнение дерева и его поддержание того стоят, но для одиночных или даже сотен или тысяч проверок O (n) идеально подходит. Другим вариантом, который не требует добавления зависимостей, будет сортировка массива или сохранение его в отсортированном порядке, и в этом случае можно использовать операцию бинарного поиска O (lg n) для проверки включения.
говорящий
1
@speakingcode, вы можете быть правы с прагматической точки зрения. Но ОП просит «проверить, существует ли значение, не более, без зацикливания». Когда я писал этот ответ, здесь было много прагматичных решений, но ни одно из них не отвечало бы буквальным требованиям автора. Ваше наблюдение, что BST связаны с попытками, является правильным, но для строк trie - это правильный инструмент для работы, даже Википедия знает это очень много . Сложность построения и поддержки хорошо реализованного дерева удивительно благоприятна.
Борис Ститницкий
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

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

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

Недостатком является то, что наборы и хэш-ключи могут включать только уникальные элементы, и если вы добавите много элементов, Ruby придется перефразировать все это после определенного количества элементов, чтобы построить новую карту, которая соответствует большему пространству клавиш. Чтобы узнать больше об этом, я рекомендую вам посмотреть " MountainWest RubyConf 2014 - Big O in Homemade Hash" от Натана Лонга ".

Вот эталон:

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)
Киммо Лехто
источник
Если вы используете функцию обнаружения, то вы можете хотя бы уменьшить зацикливание. Функция обнаружения остановится на первом «обнаруженном» элементе (блок, переданный для элемента, оценивается как true). Кроме того, вы можете указать обнаруживать, что делать, если ничего не обнаружено (вы можете передать лямбду).
aenw
1
@aenw не include?останавливается при первом ударе?
Киммо Лехто
ты абсолютно прав. Я так привык использовать обнаружение, что я забыл это о включении. спасибо за ваш комментарий - это гарантировало, что я обновил свои знания.
aenw
include?останавливается при первом попадании, но если это попадание находится в конце списка .... Любое решение, использующее массив для хранения, будет иметь ухудшающуюся производительность по мере роста списка, особенно когда необходимо найти элемент в конце список. Хэш и Сет не имеют такой проблемы, как и упорядоченный список и бинарный поиск.
Жестянщик
Это почти то, о чем этот ответ был прежде всего :)
Киммо Лехто
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

Обратите внимание на маленький * в предложении when это проверяет членство в массиве.

Применяется все обычное магическое поведение оператора splat, поэтому, например, если на arrayсамом деле это не массив, а отдельный элемент, он будет соответствовать этому элементу.

akuhn
источник
Хорошо знать..!
Кэри Свовеланд,
Это также было бы медленной первой проверкой в ​​выписке по делу, так что я бы использовал ее при последней whenвозможности, чтобы другие, более быстрые проверки, были быстро отсеяны.
Жестянщик
9

Есть несколько способов сделать это. Вот некоторые из них:

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

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false
Sumit
источник
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) операция 

Тип доступа Получает доступ к Hash [key], если он проходит через каждый элемент
                        возвращает любое значение массива до
                        true возвращается к находкам, значение в массиве
                        Hash # has_key? вызов
                        вызов    

Для одной проверки времени , используя include?отлично

aqfaridi
источник
Вы до сих пор не просматриваете массив, чтобы преобразовать его в хеш?
chrisjacob
2
да, я сделал, но теперь у меня есть предварительно вычисленный ответ, чтобы проверить, существует ли какой-либо элемент в массиве или нет. Теперь, в следующий раз, когда я буду искать другое слово, для запроса потребуется O (1), верно, хотя предварительное вычисление потребует O (n). На самом деле я использовал включить? в моем приложении внутри цикла для проверки того, существует ли конкретный элемент в массиве или нет, это снижает производительность, он проверял в ruby-prof, что было узким местом
aqfaridi
1
.... но O (n) пространство, а также, нет, это O (n) время, потому что, как правильно указывает @ chris72205, вы должны сначала перебрать свой массив. O (1) + O (n) = O (n). Так на самом деле, это намного хуже, чем включать?
Рамбатино
Чувак, я только что сказал, что не используйте include внутри цикла, так как единственная проверка с использованием include - это нормально. Поэтому, пожалуйста, сначала прочитайте сценарий использования, лучше, если вам нужно проверять несколько раз внутри цикла.
Акфариди
Преобразование массива в хеш требует больших затрат, но использование массива для поиска является неправильной структурой. Массивы лучше в виде очередей, где порядок может быть важен; Хэши и наборы лучше, когда важны включение / существование / уникальность. Начните с правильного контейнера, и проблема в споре.
Жестянщик
4

Это скажет вам не только, что он существует, но и сколько раз он появляется:

 a = ['Cat', 'Dog', 'Bird']
 a.count("Dog")
 #=> 1
user3245240
источник
12
Нет смысла использовать это, если вы не хотите знать, сколько раз он появляется, хотя, поскольку .any?он вернется, как только найдет первый соответствующий элемент, .countбудет всегда обрабатывать весь массив.
Заз
Хотя это технически покажет, существует ли что-то, это также НЕ правильный способ сделать это, если вы заботитесь о скорости.
Жестянщик
4

Что бы это ни стоило, документы Ruby - удивительный ресурс для таких вопросов.

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

Если вы работаете с большим (отсортированным) массивом, я бы подумал написать алгоритм двоичного поиска, который не должен быть слишком сложным и имеет наихудший случай O (log n).

Или, если вы используете Ruby 2.0, вы можете воспользоваться bsearch.

davissp14
источник
3
Бинарный поиск предполагает, что массив отсортирован (или упорядочен в некоторой форме), что может быть дорогостоящим для больших массивов, часто сводя на нет преимущество.
Жестянщик
Бинарный поиск также требует, чтобы все пары элементов были сопоставимы <=>, что не всегда так. Предположим, например, что элементы массива были хешами.
Кэри Свовеланд,
1
@CarySwoveland должно быть более или менее подразумеваемым, что элементы в отсортированном массиве сопоставимы.
davissp14
4

Ты можешь попробовать:

Пример: если Cat и 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?
xlembouras
источник
3
Любые? также принимает блоки: ['cat', 'dog', 'horse']. any? {| x | x == 'собака'}
майконас
3
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true
Рахул Патель
источник
['Cat', nil, 'Dog'].detect { |x| x == nil } #=> nil, Был nilнайден?
Кэри Свовеланд,
2

Как насчет этого?

['Cat', 'Dog', 'Bird'].index('Dog')
ajahongir
источник
Он все еще будет перебирать массив, чтобы найти элемент. Затем он должен вернуть индекс этого элемента.
Жестянщик
2

Если вы пытаетесь сделать это в модульном тесте MiniTest , вы можете использовать assert_includes. Пример:

pets = ['Cat', 'Dog', 'Bird']
assert_includes(pets, 'Dog')      # -> passes
assert_includes(pets, 'Zebra')    # -> fails 
Джон Шнайдер
источник
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}[,|.| |]/}
walt_die
источник
1
Ваше регулярное выражение /[,|.| |]#{v.to_s}[,|.| |]/заставляет меня думать, что вы хотели найти «название действия, окруженное одним из: запятая, точка, пробел или вообще ничего», но есть некоторые тонкие ошибки. "|update|"вернется [:update]и "update"вернется []. Классы символов ( [...]) не используют pipe ( |) для разделения символов. Даже если мы изменим их на groups ( (...)), вы не сможете найти пустой символ. Итак, регулярное выражение, которое вы, вероятно, хотели,/(,|\.| |^)#{v.to_s}(,|\.| |$)/
bkDJ
регулярное выражение в порядке (проверено rubular.com) - и @Rambatino: почему, например, Amazon Echo;) Вы можете сказать: «пожалуйста, добавьте курицу в мой список покупок» (и - ну, тогда вам придется добавить: добавить к массиву, но я думаю, вы понимаете суть его;)
walt_die
1
msgstr "регулярное выражение в порядке (проверено rubular.com)". Это не хорошо. Ваше регулярное выражение не будет соответствовать ключевому слову в начале или конце строки (например, «обновить профиль моего брата»). Если вы не хотите совпадать с началом или концом, тогда ваше регулярное выражение все еще не в порядке, потому что класс символов по обе стороны от ключевого слова должен быть/[,. ]/
bkDJ
Как говорит @bkDJ, регулярное выражение неверно. rubular.com/r/4EG04rANz6KET6
Железный человек
1

Если вы хотите вернуть значение не просто true или false, используйте

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

Это вернет «Собаку», если она существует в списке, в противном случае ноль.

gitb
источник
Или использовать , array.any?{|x| x == 'Dog'}если вы действительно хотите , истина / ложь (не значение), но и хотят , чтобы сравнить с блоком , как это.
mahemoff
0

Вот еще один способ сделать это:

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

present = arr.size != (arr - [e]).size
Wand Maker
источник
1
Это ужасно неэффективный способ сделать это! Я не опровергаю, потому что это технически некорректно, и кто-то может узнать что-то о Ruby, прочитав его, но есть много лучших ответов выше.
Джибберия
Конус, вы можете упростить arr != arr - [e]. arr & [e] == [e]другой путь в том же духе.
Кэри Свовеланд,
@CarySwoveland Не смейся над шляпой волшебника ;-)
Wand Maker
Я имел в виду голову волшебника, а не шляпу, с величайшим уважением.
Кэри Свовеланд,
0
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
Мэтью Морис
источник
0

у него есть много способов найти элемент в любом массиве, но самый простой способ - «в?» метод.

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
mgidea
источник
-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

В основном результаты говорят мне, чтобы я использовал Set для всего, если я собираюсь искать включение, если я не могу гарантировать, что первый элемент - тот, который я хочу, что не очень вероятно. При вставке элементов в хеш есть некоторые издержки, но время поиска намного быстрее, я не думаю, что это когда-либо стоит учитывать. Опять же, если вам нужно найти его, не используйте массив, используйте набор. (Или хэш.)

Чем меньше Array, тем быстрее будут работать методы Array, но они все равно не будут отставать, хотя в небольших массивах разница может быть крошечной.

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

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

Более подробная информация о Fruity и его методологии доступна в README .

Жестянщик
источник