Wie kann ich Array # delete verwenden, während ich über das Array iteriere?

71

Ich habe ein Array, über das ich iterieren und einige der Elemente löschen möchte. Das funktioniert nicht:

a = [1, 2, 3, 4, 5]
a.each do |x|
  next if x < 3
  a.delete x
  # do something with x
end
a #=> [1, 2, 4]

Ich möchte asein [1, 2]. Wie kann ich das umgehen?

Adrian
quelle
Mögliches Duplikat des Löschens beim Iterieren in Ruby?
Sschuberth

Antworten:

120

a.delete_if { |x| x >= 3 }

Siehe Methodendokumentation hier

Aktualisieren:

Sie können mit x im Block umgehen:

a.delete_if do |element|
  if element >= 3
    do_something_with(element)
    true # Make sure the if statement returns true, so it gets marked for deletion
  end
end
Chubas
quelle
1
Ich muss etwas tun, xwenn es gelöscht wird. Soll ich das in den Block legen?
Adrian
1
@Adrian Oder du könntest verwenden reject!.
XåpplI'-I0llwlg'I -
2
Eine zu beachtende Sache ist, dass zurückgegeben reject!wird, nilwenn keine Elemente abgelehnt wurden, während delete_ifdas ursprüngliche Array zurückgegeben wird.
Jan Klimo
7

Sie müssen nicht aus dem Array löschen, sondern können es filtern:

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

b = a.select {|x| x < 3}

puts b.inspect # => [1,2]

b.each {|i| puts i} # do something to each here
Joc
quelle
Es tut mir leid, einen Zombie-Beitrag zu erstellen, aber Sie sind neugierig, ob b = eine Teilmenge eines Ansatzes die Speichernutzung erhöht oder ob interne Zeiger oder etwas verwendet werden, um auf die ursprünglichen a-Elemente zu verweisen? Grund, den ich frage, ist, dass mein Anwendungsfall ziemlich speicherempfindlich ist, so neugierig, ob das Löschen aus dem ursprünglichen Array oder die Auswahl eine bessere Methode ist ...
Gorlaz
@gorlaz .selectgibt ein neues Ruby-Objekt zurück, das mehr Speicher benötigt. Sehen Sie dieses Beispiel pastebin.com/23e10bKy
lacostenycoder
Dies ist auch "funktionaler" in dem Sinne, dass Daten nicht verändert werden.
Pedz
4

Ich habe diese Frage vor nicht allzu langer Zeit gestellt.

Löschen beim Iterieren in Ruby?

Es funktioniert nicht, weil Ruby die .eachSchleife verlässt , wenn er versucht, etwas zu löschen. Wenn Sie einfach Dinge aus dem Array löschen möchten, delete_iffunktioniert dies, aber wenn Sie mehr Kontrolle wünschen, funktioniert die Lösung, die ich in diesem Thread habe, obwohl sie irgendwie hässlich ist.

Jesse Jashinsky
quelle
9
Das Löschen unterbricht die Schleife nicht. eachiteriert effektiv mithilfe des Index. Wenn Sie also löschen, sind die Indizes unterschiedlich, und bei der nächsten Iteration werden die neuen Indizes verwendet. Versuchen Sie Folgendes : arr = [1,2,3,4,5,6]; arr.each_with_index {|e,i| p [arr, e, i]; next if e < 3; arr.delete e }. Wenn Element 3 erreicht ist, ist der Index 2. Nachdem 3 gelöscht wurde, erhöht die nächste Iteration den Index auf 3 und das Element ist 5. Daher wird 4 übersprungen. 6 wird aus dem gleichen Grund übersprungen.
Kelvin
3

Eine andere Möglichkeit ist die Verwendung von reject!, was wohl klarer ist, da es eine hat, !was bedeutet, dass "dies das Array ändert". Der einzige Unterschied besteht darin, dass zurückgegeben reject!wird, nilwenn keine Änderungen vorgenommen wurden.

a.delete_if {|x| x >= 3 }

oder

a.reject! {|x| x >= 3 }

wird beides gut funktionieren.

AlexChaffee
quelle