it-swarm.com.de

Überprüfen Sie, ob ein Wert in einem Array in Ruby vorhanden ist.

Ich habe einen Wert 'Dog' und ein Array ['Cat', 'Dog', 'Bird'].

Wie überprüfe ich, ob es im Array vorhanden ist, ohne es zu durchlaufen? Gibt es eine einfache Möglichkeit zu überprüfen, ob der Wert existiert, mehr nicht?

1227
user211662

Du suchst nach include? :

_>> ['Cat', 'Dog', 'Bird'].include? 'Dog'
=> true
_
1834
Brian Campbell

Es gibt eine _in?_ -Methode in ActiveSupport (Teil von Rails) seit Version 3.1, auf die @campaterson hingewiesen hat. Also innerhalb von Rails, oder wenn Sie _require 'active_support'_, können Sie schreiben:

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

OTOH, es gibt keinen Operator in oder eine Methode _#in?_ in Ruby selbst, obwohl dies zuvor vorgeschlagen wurde insbesondere von Yusuke Endoh a top Notch Mitglied von Ruby-Core.

Wie von anderen betont, existiert die umgekehrte Methode include? für alle Enumerables einschließlich Array, Hash, Set, Range:

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

Beachten Sie, dass, wenn Sie viele Werte in Ihrem Array haben, alle nacheinander überprüft werden (d. H. O(n)), während die Suche nach einem Hash eine konstante Zeit ist (d. H. O(1)). Wenn Ihr Array beispielsweise konstant ist, ist es eine gute Idee, stattdessen ein Set zu verwenden. Z.B:

_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
_

Ein schneller Test zeigt, dass der Aufruf von _include?_ für ein 10-Element Set ungefähr 3,5-mal schneller ist als der Aufruf für das entsprechende Array (wenn das Element nicht gefunden wird) ).

Ein letzter Schlussbemerkung: Seien Sie vorsichtig, wenn Sie _include?_ auf einem Range verwenden. Es gibt Feinheiten. Lesen Sie also das Dokument und vergleichen Sie mit _cover?_. ...

234

Versuchen

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

Benutze Enumerable#include:

a = %w/Cat Dog Bird/

a.include? 'Dog'

Oder wenn eine Reihe von Tests durchgeführt werden,1 Sie können die Schleife loswerden (die sogar include? hat) und von O (n) zu O (1) gehen mit:

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

1. Ich hoffe, dass dies offensichtlich ist, aber um Einwände abzuwenden: Ja, für ein paar Lookups dominieren die Hash- [] und Transponierungsoperationen das Profil und sind jeweils O (n) für sich.
48
DigitalRoss

Wenn Sie durch einen Block überprüfen möchten, könnten Sie irgendwelche versuchen? oder alle?.

%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  

Details finden Sie hier: http://Ruby-doc.org/core-1.9.3/Enumerable.html
Meine Inspiration kommt von hier: https://stackoverflow.com/a/10342734/576497

44
Van

Ruby hat 11 Methoden, um Elemente in einem Array zu finden.

Der bevorzugte ist include?

Oder für wiederholten Zugriff ein Set erstellen und dann include? oder member? aufrufen

Hier sind alle von ihnen,

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

Alle von ihnen geben einen trueish-Wert zurück, wenn das Element vorhanden ist.

include? ist die bevorzugte Methode. Intern wird eine C-Sprache for-Schleife verwendet, die unterbrochen wird, wenn ein Element mit den internen rb_equal_opt/rb_equal -Funktionen übereinstimmt. Effizienter kann es nicht werden, wenn Sie nicht einen Satz für wiederholte Mitgliedschaftsprüfungen erstellen.

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? ist in der Klasse Array nicht neu definiert und verwendet eine nicht optimierte Implementierung aus dem Modul Enumerable, die alle Elemente buchstäblich auflistet.

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;
}

Übersetzt in Ruby Code bewirkt dies Folgendes

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

Beide include? und member? haben O(n) Zeitkomplexität, da beide das Array nach dem ersten Auftreten des erwarteten Werts durchsuchen.

Wir können eine Menge verwenden, um die Zugriffszeit O(1) zu erhalten, und zwar auf Kosten der Notwendigkeit, zuerst eine Hash-Darstellung des Arrays zu erstellen. Wenn Sie die Mitgliedschaft im selben Array wiederholt überprüfen, kann sich diese Anfangsinvestition schnell auszahlen. Set ist nicht in C implementiert, sondern als einfache Ruby Klasse, dennoch macht die O(1) Zugriffszeit des zugrunde liegenden @hash dies sinnvoll.

Hier ist die Implementierung der Klasse 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

Wie Sie sehen, erstellt die Klasse Set nur eine interne @hash -Instanz, ordnet alle Objekte true zu und überprüft dann die Mitgliedschaft mit Hash#include?, das mit O(1) implementiert wird. Zugriffszeit in der Klasse Hash.

Ich werde die anderen 7 Methoden nicht diskutieren, da sie alle weniger effizient sind.

Es gibt sogar noch mehr Methoden mit O(n) Komplexität als die oben aufgelisteten 11, aber ich habe beschlossen, sie nicht aufzulisten, da das gesamte Array gescannt wird, anstatt beim ersten Treffer zu brechen.

Verwenden Sie diese nicht,

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

Mehrere Antworten lassen auf Array#include? schließen, aber es gibt eine wichtige Einschränkung: Wenn man sich die Quelle ansieht, führt sogar Array#include? eine Schleife durch:

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;
}

Sie können die Word-Präsenz testen, ohne eine Schleife zu erstellen, indem Sie ein trie für Ihr Array erstellen. Es gibt viele Trie-Implementierungen (google "Ruby Trie"). Ich werde rambling-trie in diesem Beispiel verwenden:

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 }

Und jetzt sind wir bereit, das Vorhandensein verschiedener Wörter in Ihrem Array zu testen, ohne es in O(log n) -Zeit mit der gleichen syntaktischen Einfachheit wie Array#include? zu durchlaufen, wobei sublinear Trie#include? verwendet wird:

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

Wenn Sie keine Schleife ausführen möchten, können Sie dies nicht mit Arrays tun. Sie sollten stattdessen ein Set verwenden.

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

Sets funktionieren intern wie Hashes, daher muss Ruby nicht die Auflistung durchlaufen, um Elemente zu finden, da, wie der Name schon sagt, Hashes der Schlüssel generiert und eine Speicherzuordnung erstellt werden, auf die jeder Hash verweist ein bestimmter Punkt in der Erinnerung. Das vorherige Beispiel mit einem Hash:

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

Der Nachteil ist, dass Sets und Hash-Schlüssel nur eindeutige Elemente enthalten können. Wenn Sie viele Elemente hinzufügen, muss Ruby das Ganze nach einer bestimmten Anzahl von Elementen erneut aufbereiten, um eine neue Karte zu erstellen, die zu einer größeren Karte passt Schlüsselraum. Um mehr darüber zu erfahren, empfehle ich Ihnen, sich MountainWest RubyConf 2014 - Big O in einem hausgemachten Hash von Nathan Long anzuschauen

Hier ist ein Benchmark:

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

Und die Ergebnisse:

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

Dies ist eine andere Möglichkeit: Verwenden Sie die Array # -Indexmethode.

Es gibt den Index des ersten Vorkommens des Elements im Array zurück.

beispiel:

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

index () kann auch einen Block einnehmen

zum Beispiel

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

geben Sie hier den Index des ersten Wortes im Array zurück, das den Buchstaben 'o' enthält.

15
Zack Xu

Lustige Tatsache,

Sie können * verwenden, um die Array-Mitgliedschaft in case Ausdrücken zu überprüfen.

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

Beachten Sie den kleinen * in der when-Klausel, der die Mitgliedschaft im Array überprüft.

Es gilt das übliche magische Verhalten des splat-Operators. Wenn also beispielsweise array kein Array, sondern ein einzelnes Element ist, entspricht es diesem Element.

9
akuhn

Es gibt mehrere Möglichkeiten, dies zu erreichen. Einige davon sind wie folgt:

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

2.in? a  #=> true

8.in? a #=> false

a.member? 1 #=> true

a.member? 8 #=> false
8
sumit

Dies sagt Ihnen nicht nur, dass es existiert, sondern auch, wie oft es erscheint:

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

Wenn Sie mehrere Male nach einem Schlüssel suchen müssen, konvertieren Sie arr in hash und checken Sie jetzt O (1) ein.

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

Leistung von Hash # has_key? versus Array # include?

 Parameter Hash # has_key? Array # include 
 
 Zeitkomplexität O(1) Operation O(n) Operation 
 
 Zugriffstyp Zugriffe Hash [Schlüssel], wenn es durch jedes Element 
 Iteriert, gibt einen beliebigen Wert des Arrays zurück, bis es 
 True zurückgibt, um den Wert in Array 
 Zu finden. Hash # has_key? anrufen 
 anrufen 

Für eine einmalige Überprüfung ist die Verwendung von include? in Ordnung

4
aqfaridi

Wenn Sie mehr Werte im Sinn haben ... können Sie versuchen:

Beispiel: Wenn Katze und Hund im Array vorhanden sind:

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

Anstatt von:

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

Hinweis: Mitglied? und enthalten? sind gleich.

Dies kann die Arbeit in einer Zeile erledigen!

Für das, was es wert ist, sind die Ruby-Dokumente eine erstaunliche Ressource für diese Art von Fragen.

Ich würde auch die Länge des Arrays notieren, das Sie durchsuchen. Die include? -Methode führt eine lineare Suche mit der Komplexität O(n) durch, die je nach Größe des Arrays ziemlich hässlich werden kann.

Wenn Sie mit einem großen (sortierten) Array arbeiten, würde ich überlegen, einen binären Suchalgorithmus zu schreiben, der nicht allzu schwierig sein sollte und den schlimmsten Fall von O (log n) hat.

Oder wenn Sie Ruby 2.0 verwenden, können Sie bsearch nutzen.

4
davissp14

Wenn wir include? nicht verwenden möchten, funktioniert dies auch:

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

Es geht auch anders herum!

Angenommen, das Array ist [: editieren,: aktualisieren,: erstellen,: anzeigen] - nun, vielleicht das gesamte sieben tödliche/erholsame Sünden :)

Und weiter spielen mit der Idee ziehen eine gültige Aktion aus einem String - sagen wir

mein Bruder möchte, dass ich sein Profil aktualisiere

Lösung

[ :edit, :update, :create, :show ].select{|v| v if "my brother would like me to update his profile".downcase =~ /[,|.| |]#{v.to_s}[,|.| |]/}
3
walt_die
['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}
=> "Dog"
!['Cat', 'Dog', 'Bird'].detect { |x| x == 'Dog'}.nil?
=> true
3
Rahul Patel

Wenn Sie dies in einem MiniTest Unit-Test versuchen, können Sie assert_includes verwenden. Beispiel:

_pets = ['Cat', 'Dog', 'Bird']
assert_includes(pets, 'Dog')      # -> passes
assert_includes(pets, 'Zebra')    # -> fails 
_
2
Jon Schneider

Wie wäre es auf diese Weise?

['Cat', 'Dog', 'Bird'].index('Dog')
2
ajahongir

Wenn Sie den Wert nicht nur true oder false zurückgeben möchten, verwenden Sie

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

Dies gibt "Hund" zurück, wenn es in der Liste vorhanden ist, andernfalls null.

1
gitb

wenn Sie include nicht verwenden möchten? Sie können das Element zuerst in ein Array einschließen und dann überprüfen, ob das umschlossene Element dem Schnittpunkt des Arrays und des umschlossenen Elements entspricht. Dies gibt einen booleschen Wert zurück, der auf Gleichheit basiert.

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

Hier ist eine weitere Möglichkeit, dies zu tun:

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

present = arr.size != (arr - [e]).size
0
Wand Maker
array = [ 'Cat', 'Dog', 'Bird' ]
array.include?("Dog")
0
Matthew Maurice