4.8 Arrays 

Arrays in Ruby haben Indizes vom Typ Integer. Der Index beginnt standardmäßig bei 0. Wenn Sie ein neues Array erzeugen, können Sie seine Größe über einen Parameter angeben, müssen das aber nicht, denn Arrays passen ihre Grösse dynamisch der zu speichernden Datenmenge an.
Ein Array kennt zu jedem Zeitpunkt seine aktuelle Größe, so dass wir uns nicht darum kümmern müssen, diesen Wert aufwendig zu ermitteln, sondern wir können ihn jederzeit ganz einfach abfragen. Die Klasse Array stellt eine Vielzahl von Methoden zur Verfügung, um auf Arrays zuzugreifen, sie zu durchsuchen, sie miteinander zu verknüpfen oder anderswie mit ihnen zu arbeiten.
Ein Array erzeugen 

[]
Die Klassenmethode [] erzeugt ein neues Array. Es gibt 3 Möglichkeiten, diese Methode zu nutzen und ihr die Werte des Arrays zu übergeben:
>> a1 = ["a","b","c","d"] => [a, b, c, d] >> a2 = Array.[]("a","b","c","d") => [a, b, c, d] >> a3 = Array["a","b","c","d"] => [a, b, c, d]
new
Sie können auch über die Methode new ein neues Array erzeugen. Der Methode new können Sie keinen, einen oder zwei Parameter übergeben. Über den ersten Parameter können Sie die Größe des Arrays (Anzahl der Elemente) festlegen und über den zweiten den Wert dieser Elemente:
>> a4 = Array.new => [] >> a5 = Array.new(2) => [nil, nil] >> a6 = Array.new(2, "test") => ["test", "test"]
Im letzten Beispiel sieht es so aus, als ob in dem Array a6 zwei verschiedene Objekte abgelegt wurden. Dem ist aber nicht so. Die beiden Werte verweisen auf das gleiche Objekt. Sprich, wenn Sie eines der Elemente verändern, wirkt sich das auf alle Elemente aus.
>> a6[0].upcase! => "TEST" >> a6 => ["TEST", "TEST"]
Block verwenden
Um diese Situation zu vermeiden, können Sie beim Erzeugen des Arrays einen Block verwenden, denn der Block wird für jedes Element neu ausgeführt und erzeugt deshalb voneinander unabhängige Objekte:
>> a7 = Array.new(2) {"test"} => ["test", "test"] >> a7[0].upcase! => "TEST" >> a7 => ["TEST", "test"]
Auf Arrayelemente zugreifen 

Um ein Arrayelement anzulegen und um auf ein Arrayelement zuzugreifen, nutzen Sie die beiden Methoden [] und +[]=+. Beiden Methoden können Sie als Index einen Integer-Wert übergeben oder zwei Integer-Werte, die dann den Startpunkt und eine Länge repräsentieren, oder Sie können den Methoden einen Bereich übergeben. Ein negativer Index zählt vom Ende des Arrays. Auf das letzte Element eines Arrays kann z. B. mit dem Index »-1« zugegriffen werden.
at
Die Instanzmethode at funktioniert genauso wie der Zugriff über [] mit einem Parameter, ist jedoch dadurch schneller in der Ausführung:
>> a = ["Januar", "Februar", "Maerz", "April"] => ["Januar", "Februar", "Maerz", "April"] >> a[1] => "Februar" >> a.at(-1) => "April" >> a[4] => nil >> a[1,2] => ["Februar", "Maerz"] >> a[3] = "Mai" => "Mai" >> a[-1] => "Mai" >> a => ["Januar", "Februar", "Maerz", "Mai"]
slice
Die Methode slice ist ein Alias für die Methode []:
>> a.slice(2) => "Maerz" >> a.slice(1,2,3) => ["Februar", "Maerz", "Mai"]
Wenn Sie ein Element an einer Position über die Länge des Arrays hinaus einfügen, passt das Array automatisch seine Größe an, sprich, es wird größer und setzt eventuelle nicht besetzte Positionen dazwischen auf »nil«. Gleiches passiert, wenn Sie für einen Bereich mehr Werte übergeben als eigentlich für den Bereich möglich wären:
>> a = ["M", "HH", "B"] => ["M", "HH", "B"] >> a[1..2] = ["TR", "D"] => ["TR", "D"] >> a => ["M", "TR", "D"] >> a[1..2] = ["TR", "D", "K"] => ["TR", "D", "K"] >> a => ["M", "TR", "D", "K"] >> a[10] = "HB" => "HB" >> a => ["M", "TR", "D", "K", nil, nil, nil, nil, nil, nil, "HB"]
Wenn Sie einem Array-Element wiederum ein Array zuweisen, wird dieses in das vorhandene Array verschachtelt. Anders verhält es sich, wenn Sie einem Bereich in einem Array ein Array übergeben. Dann werden die Elemente des übergebenen Arrays an den Positionen des angegebenen Bereichs eingefügt:
>> a = ["Birne", "Apfel", "Banane"] => ["Birne", "Apfel", "Banane"] >> a[0] = ["Paprika", "Blumenkohl"] => ["Paprika", "Blumenkohl"] >> a => [["Paprika", "Blumenkohl"], "Apfel", "Banane"] >> a[1..2] = ["Ananas", "Trauben", "Datteln"] => ["Ananas", "Trauben", "Datteln"] >> a => [["Paprika", "Blumenkohl"], "Ananas", "Trauben", "Datteln"]
first, last
Die Methoden first und last liefern das erste bzw. letzte Element eines Arrays. Wenn das Array leer ist, geben sie nil zurück:
>> a = ["Birne", "Apfel", "Banane"] => ["Birne", "Apfel", "Banane"] >> a.first => "Birne" >> a.last => "Banane"
Ruby kennt eine Kurzschreibweise, um ein Array mit Zeichenketten aufzubauen:
>> %w[Birne Apfel Banane] => ["Birne", "Apfel", "Banane"]
Durch das vorangestellte %w sparen Sie die Eingabe der Hochkommata und der Kommata als Trennzeichen zwischen den einzelnen Werten.
Mehrere Elemente auslesen
Wir haben eben gesehen, dass es möglich ist, durch Übergabe eines Bereichs oder durch die Definition eines Startpunktes und einer bestimmten Länge an die Methode [] mehrere Elemente eines Arrays gleichzeitig auszulesen. Die Methode values_at bietet uns noch weitere Möglichkeiten, mehrere Elemente gleichzeitig auszulesen. Als Parameter empfängt sie eine Liste von Indizes. Die Methode values_at kommt immer dann zum Einsatz, wenn die auszulesenden Elemente nicht zusammenstehen, also immer dann, wenn die Angabe eines Bereichs nicht mehr ausreicht:
>> a = [1,2,3,4,5,6,7,8,9] => [1, 2, 3, 4, 5, 6, 7, 8, 9] >> a.values_at(2,5,8) => [3, 6, 9] >> a.values_at(0,2..5,8) => [1, 3, 4, 5, 6, 9]
Die Methode values_at hieß in älteren Versionen von Ruby indices (Alias indexes). Von der aktuellen Ruby-Version werden diese beiden Methoden nicht mehr unterstützt.
Auf die Länge eines Arrays zugreifen 

length, size
Die Methode length und ihr Alias size liefern die Anzahl der Elemente in einem Array zurück. Wie wir das von ähnlichen Methoden anderer Programmiersprachen kennen, ist die Anzahl um einen Wert höher als der letzte Index in dem Array:
>> a = %w[rot gelb gruen] => ["rot", "gelb", "gruen"] >> a.length => 3 >> a.size => 3
nitems
Die Methode nitems zählt auch die Elemente eines Arrays, mit Ausnahme der nil-Elemente:
>> a = ["rot", nil, nil, "gelb", nil, "gruen"] => ["rot", nil, nil, "gelb", nil, "gruen"] >> a.length => 6 >> a.nitems => 3
Arrays vergleichen 

Modul Comparable
Arrays miteinander zu vergleichen, ist eine knifflige Angelegenheit. Nicht so, wenn man das Modul Comparable dazu nutzt. Die Methoden eines Moduls kann man in anderen Klassen nutzen, indem man das Modul inkludiert (Mehr Informationen zu Module siehe Abschnitt 4.11):
class Array include Comparable end a = [5, 6, 7] b = [5, 6, 7, 8] c = [5, 6, 7] if a < b puts "a < b" end if a == c puts "a == c" end # => a < b # => a == c
Ein Array sortieren 

sort
Die einfachste Art und Weise, ein Array zu sortieren, ist, die Methode sort wie folgt zu benutzen:
>> woerter = %w(RailsAir ist eine erfolgreiche Airline) => ["RailsAir", "ist", "eine", "erfolgreiche", "Airline"] >> woerter.sort => ["Airline", "RailsAir", "eine", "erfolgreiche", "ist"] >> woerter => ["RailsAir", "ist", "eine", "erfolgreiche", "Airline"] >> woerter.sort! => ["Airline", "RailsAir", "eine", "erfolgreiche", "ist"] >> woerter => ["Airline", "RailsAir", "eine", "erfolgreiche", "ist"]
Auch für Methoden der Klasse Array gilt: Methoden, die mit einem Ausrufezeichen enden, verändern das Ausgangsobjekt.
Die Methode sort ist nur dann fehlerfrei einsetzbar, wenn alle Elemente innerhalb des Arrays vom gleichen Datentyp sind. Anderenfalls liefert sie einen TypeError zurück:
>> a = ["zeichen", "zahl", 3] => ["zeichen", "zahl", 3] >> a.sort ArgumentError: comparison of String with 3 failed from (irb):2:in 'sort' from (irb):2
Mit einem Block aufrufen
Das können Sie verhindern, indem Sie in einem solchen Fall die Methode sort mit einem Block aufrufen und in diesem Block jedes Element in den gleichen Datentyp umwandeln (z. B. mit der Methode to_s in einen String):
>> a.sort {|x,y| x.to_s <=> y.to_s} => [3, "zahl", "zeichen"]
Die Sortierung erfolgt nach der Umwandlung in einen String auf ASCII-Basis.
Natürlich haben Sie Recht: Bei diesem Beispiel handelt es sich um ein rein theoretisches Beispiel, da es nicht viel Sinn macht, ein Array, das Elemente unterschiedlicher Datentypen enthält, zu sortieren. Trotzdem möchten wir Ihnen noch kurz erläutern, was da genau passiert:
Der Block liefert bei jedem Aufruf -1, 0 oder 1 zurück. Im Falle von -1, was bedeutet, dass x kleiner y ist, werden die beiden Elemente vertauscht. Das bedeutet wiederum, dass wenn wir ein Array in absteigender Reihenfolge sortieren möchten, wir einfach nur die Variablen innerhalb des Vergleichs vertauschen müssen:
>> a.sort {|x,y| y.to_s <=> x.to_s} => ["zeichen", "zahl", 3]
sort_by
In den neueren Versionen von Ruby enthält das Modul Enumerable die Methode sort_by . Dieses Modul ist standardmäßig in der Klasse Array inkludiert.
Die Methode sort_by setzt das ein, was Perl-Programmierer unter dem Begriff »Schwartzian Transformation« kennen (nach Randal Schwartz). Die Sortierung beruht nicht auf den zu sortierenden Elementen selbst, sondern es wird eine Art Funktion zum Sortieren angewendet. Dieser Lösungsansatz birgt zwei Probleme: Zum einen wirkt der Code umständlich, und zum anderen ergeben sich aus jedem Aufruf mehrere Plattenzugriffe, von denen jeder eine aufwendige Aktion darstellt, und der Block wird öfter als einmal aufgerufen.
Mit der sort_by -Methode kann man beide Probleme lösen. Jeder Schlüssel wird nur noch einmal berechnet und dann intern in einem Schlüssel-Daten-Paar abgespeichert. Bei kleineren Arrays könnte das die Leistungsfähigkeit verringern, aber der Code ist allemal viel schöner, und das sollte es uns wert sein.
Es gibt standardmäßig keine Methode sort_by!, aber Sie können natürlich jederzeit eine solche Methode definieren.
Nach mehreren Attributen sortieren
Aber was ist, wenn wir ein Array über mehr als ein Attribut sortieren müssen, wie z. B. Name, Alter und Größe? Sie können selbstverständlich nach mehreren Attributen, egal nach welchen, sortieren:
list = list.sort_by {|x| [x.name, x.age, x.height] }
Ein Array zufällig sortieren 

Manchmal möchten wir die Elemente in einem Array in eine zufällige Reihenfolge bringen. Sei es z. B. für ein Online-Kartenspiel oder um in einem Gewinnspiel den Usern die Fragen in einer zufälligen Reihenfolge anzuzeigen.
rand
Um das Problem zu lösen, steht uns die Methode rand aus dem Modul Kernel zur Verfügung, die uns eine zufällige Zahl liefert:
class Array def randomize self.sort_by { rand } end end x = [1, 2, 3, 4, 5] puts x.randomize # => [3, 4, 1, 5, 2]
Wenn wir ein Array-Element zufällig auswählen wollen, können wir das wie folgt lösen:
class Array def pick_random self[rand(self.length)] end end x = [1, 2, 3, 4, 5] puts x.pick_random # => 4
Nach Elementen in einem Array suchen 

Manchmal wollen wir auf ein bestimmtes Element innerhalb eines Arrays genauso zugreifen wie auf ein Element innerhalb einer Datenbank. Die gute Nachricht: Es gibt mehrere Möglichkeiten, das zu tun. Alle die, die wir Ihnen hier vorstellen, sind im Modul Enumerable definiert, das von der Klasse Array inkludiert wird.
detect
Die Methode detect findet höchstens ein Element. Sie führt einen Block aus, an den die einzelnen Elemente der Reihe nach übergeben werden, und liefert den ersten Wert, auf den die Bedingung zutrifft, als Ergebnis zurück.
>> a = [5, 21, 9, 4, 1, 8] => [5, 21, 9, 4, 1, 8] >> a.detect {|e| e % 2 == 0 } => 4 >> a.detect {|e| e % 7 == 0 } => 21
find, find_all, select
Die Methode find ist ein Alias der Methode detect . Die Methode find_all ist eine Variante der Methode find, die alle passenden Ergebnisse in einem Ergebnisarray zurückliefert. Die Methode select ist wiederum ein Alias der Methode find_all:
>> a.find {|e| e % 2 == 0} => 4 >> a.find_all {|e| e % 2 == 0} => [4, 8] >> a.select {|e| e % 2 == 0} => [4, 8]
reject
Die Methode reject ist die Komplementärmethode zu select . Sie schließt alle Elemente aus, auf die die Bedingung im Block zutrifft. Die Methode reject!, die das Originalobjekt verändert, ist auch definiert:
>> a.reject {|e| e % 2 == 0 } => [5, 21, 9, 1]
grep
Die Methode grep setzt den Operator === ein, um jedes Element mit einem vorgegebenen Muster zu vergleichen. In ihrer einfachsten Form angewendet, liefert sie ein Array mit den übereinstimmenden Ergebnissen zurück. Das Muster kann außer einem regulären Ausdruck auch z. B. ein Bereich sein. Der Name grep kommt aus der Unix-Welt und steht in Bezug zu dem alten Editorbefehl g\/re\/p.
>> a = %w[taschenbuch buchhandlung buecherei buecher] => ["taschenbuch", "buchhandlung", "buecherei", "buecher"] >> a.grep(/buch/) => ["taschenbuch", "buchhandlung"]
min, max
Die beiden Methoden min und max, die im Modul Enumerable definiert sind, können verwendet werden, um den Minimal- und Maximalwert innerhalb eines Arrays zu ermitteln. Den beiden Methoden können entweder keine Parameter oder ein Block übergeben werden. Übergibt man ihnen keinen Parameter, ermitteln sie den Minimal- bzw. Maximalwert der gegebenen Werte innerhalb des Arrays. Durch Übergabe eines Blocks können wir definieren, von welchem Attribut der Elemente im Array wir den Minimal- oder Maximalwert erhalten möchten:
>> a = %w[Schottland Deutschland Luxemburg Spanien] => ["Schottland", "Deutschland", "Luxemburg", "Spanien"] >> a. min => "Deutschland" >> a.max => "Spanien" >> a.min {|x,y| x.length <=> y.length} => "Spanien" >> a.max {|x,y| x.length <=> y.length} => "Deutschland"
Angenommen, wir wollten den Index des Minimal- oder Maximalwertes ermitteln, würden wir der Methode index das Ergebnis der Methoden min oder max als Parameter übergeben. Bezugnehmend auf obiges Beispiel bedeutet das konkret:
>> a.index(a.min) => 1 >> a.index(a.max) => 3
Die Methode index kann natürlich auf jedes beliebige Element eines Arrays angewendet werden. Sollte das übergebene Element innerhalb des Arrays nicht eindeutig sein, liefert die Methode den Index des ersten Vorkommens des Elementes zurück:
>> a.index("Schottland") => 0 >> a.index("Luxemburg") => 2
Differenz zwischen zwei Arrays bestimmen 

Dieses Problem lässt sich in Ruby sehr viel einfacher lösen als in den meisten anderen Programmiersprachen:
text = %w[hier sind die gesuchten Wörter Schottland Luxemburg] vergleich = %w[die gesuchten Wörter sind hier nicht enthalten] unbekannt = text - vergleich # ["Schottland", "Luxemburg"]
nil-Werte aus einem Array entfernen 

compact
Die Methode compact oder compact! entfernt nil -Elemente aus einem Array:
>> a = [7, 9, nil, 5, nil, 3, 1] => [7, 9, nil, 5, nil, 3, 1] >> a.compact => [7, 9, 5, 3, 1]
Bestimmte Array-Elemente entfernen 

delete_at
Es gibt viele Möglichkeiten, bestimmte Elemente aus einem Ruby-Array zu entfernen. Wenn Sie ein Element an einer bestimmten Position entfernen möchten, ist die Methode delete_at eine gute Wahl. Die Methode erwartet eine Index-Position als Parameter. Ist die übergebene Position außerhalb des Bereichs des Arrays, liefert die Methode nil zurück:
>> a = [7, 9, 5, 3, 1] => [7, 9, 5, 3, 1] >> a.delete_at(3) => 3 >> a => [7, 9, 5, 1] >> a.delete_at(10) => nil
delete
Wenn wir alle Elemente mit einem bestimmten Wert aus einem Array entfernen möchten, kommt die Methode delete zum Einsatz. Sie liefert den Wert der gelöschten Elemente oder, sollte der übergebene Wert nicht existieren, nil zurück.
>> a = %w(Januar Februar Januar April Mai April Januar) => ["Januar", "Februar", "Januar", "April", "Mai", "April", "Januar"] >> a.delete("Januar") => "Januar" >> a => ["Februar", "April", "Mai", "April"]
Der Methode delete können Sie auch einen Block übergeben. Das Besondere dabei ist, dass der Block nur dann ausgeführt wird, wenn der zu löschende Wert nicht innerhalb des Arrays gefunden wird:
>> a.delete("April") { "existiert nicht" } => "April" >> a.delete("September") { "existiert nicht" } => "existiert nicht"
delete_if
Die Methode delete_if führt den Block für jedes Array-Element aus und löscht die Elemente, die die Bedingung im Block erfüllen. Da die Elemente aus dem Originalobjekt gelöscht werden, verhält sich delete_if ähnlich wie die Methode reject! . Trifft die Bedingung im Block auf keines der Elemente zu, liefert die Methode nil zurück:
>> texte = %w[newsletter spam gruesse angebote] => ["newsletter", "spam", "gruesse", "angebote"] >> texte.delete_if {|x| x.length == 4 } => ["newsletter", "gruesse", "angebote"]
shift, pop
Die Methoden shift und pop löschen das erste bzw. letzte Element eines Arrays:
>> a = [1, 2, 3, 4, 5] => [1, 2, 3, 4, 5] >> a.shift => 1 >> a => [2, 3, 4, 5] >> a.pop => 5 >> a => [2, 3, 4]
clear
Schließlich können Sie mit der Methode clear alle Elemente aus einem Array löschen. Das können Sie auch erreichen, indem Sie der Arrayvariablen ein leeres Array zuweisen, aber die Methode clear ist effizienter:
>> a.clear => []
Ein Array umkehren 

reverse
Nicht nur der Klasse String steht eine Methode reverse zur Verfügung, um eine Zeichenkette umzukehren, sondern auch die Klasse Array hat eine Methode reverse, um ein Array umzukehren:
>> tiere = %w[hund katze maus] => ["hund", "katze", "maus"] >> tiere.reverse => ["maus", "katze", "hund"]
Doppelte Einträge aus einem Array löschen 

uniq
Wenn Sie doppelte Einträge aus einem Array löschen möchten, können Sie das mit Hilfe der Methode uniq oder uniq! tun. Die Methode liefert ein Array ohne doppelte Einträge zurück. Befindet sich kein doppelter Eintrag in dem Array, liefert uniq nil zurück:
>> a = ["a", "b", "a", "c", "c", "d"] => ["a", "b", "a", "c", "c", "d"] >> a.uniq => ["a", "b", "c", "d"]
Iteratoren 

Ruby stellt uns eine Reihe von Methoden zur Verfügung, mit denen wir über ein Array iterieren können (Iteratoren).
each
Der Standard-Iterator der Klasse Array ist die Methode each . Die Methode erwartet einen Block als Übergabeparameter, den sie für jedes Element ausführt und dem sie das Element selbst als Parameter übergibt:
>> a = [ "a", "b", "c" ] => ["a", "b", "c"] >> a.each {|x| print x, " ++ " } a ++ b ++ c ++ => ["a", "b", "c"]
reverse_each
Schneller als reverse.each
Ein anderer nützlicher Iterator ist die Methode reverse_each, die in umgekehrter Reihenfolge als die Methode each über ein Array iteriert. Um das gleiche Ziel zu erreichen, könnten Sie auch zuerst die Methode reverse und dann die Methode each anwenden, die Methode reverse_each ist aber wesentlich schneller:
>> aussage = %w(Die ist ein Beispiel) => ["Dies", "ist", "ein", "Beispiel"] >> str = "" => "" >> aussage.reverse_each { |w| str += "#{w} "} => ["Dies", "ist", "ein", "Beispiel"] >> str => "Beispiel ein ist Dies"
each_index
Um nur über die Indizes eines Arrays zu iterieren, steht die Methode each_index zur Verfügung, die dem Block den aktuellen Index als Parameter übergibt:
>> a = [ "a", "b", "c" ] => ["a", "b", "c"] >> a.each_index {|x| print x, " ++ " } 0 ++ 1 ++ 2 ++ => ["a", "b", "c"]
each_with_index
Der Iterator each_with_index ist eine Kombination aus der Methode each und der Methode each_index . Dem Block werden zwei Parameter, das Element selbst und sein Index, übergeben:
>> a = ["a", "b", "c"] => ["a", "c", "c"] >> a.each_with_index do |x,i| >> puts "Element #{x} ist #{i}" >> end Element 0 ist a Element 1 ist b Element 2 ist c => ["a", "b", "c"]
map
Wir können aber auch mit der Methode map oder ihrem Synonym collect über ein Array iterieren. Den Methoden können wir einen Block übergeben, der für jedes Element aus dem Array ausgeführt wird. Als Ergebnis wird wieder ein Array zurückgeliefert. In unserem Beispiel möchten wir die in einem Array gespeicherten Preise um 10 % erhöhen:
preise = [2.5, 5.6, 12.10] preise.map {|preis| preis *1.1} # => [2.75, 6.16, 13.31]
Mit der Methode map oder collect können Sie auch nur bestimmte Attribute von Objekten aus einem Array ausgeben:
class Product attr_accessor :name attr_accessor :price def initialize(params) @name = params[:name] @price = params[:price] end end product1 = Product.new(:name => 'iMac', :price=>1400) product2 = Product.new(:name => 'MacBook', :price=>999) product3 = Product.new(:name => 'iPhone', :price=>499) products = [product1, product2, product3] p products.map{|product| product.name} # => ["iMac", "MacBook", "iPhone"]
any?
Mit der Methode any?, die true oder false zurückliefert, können wir abfragen, ob es ein oder mehrere Elemente in einem Array gibt, die eine bestimmte Bedingung erfüllen:
p products.any?{|product| product.price < 1000} # => true
all?
Mit der Methode all?, die ebenfalls true oder false zurückliefert, können wir abfragen, ob alle Elemente eine bestimmte Bedingung erfüllen:
p products.all?{|product| product.price < 1000} # => false
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.