Zuordnen eines Array Modifizieren nur Elemente eine bestimmte Bedingung für:
Frage
In Ruby, was ist das ausdrucksstärkste Art und Weise eine Anordnung so abzubilden, dass bestimmte Elemente geändert werden und die anderen bleiben unberührt
Dies ist ein straight-forward Weg, es zu tun:
old_a = ["a", "b", "c"] # ["a", "b", "c"]
new_a = old_a.map { |x| (x=="b" ? x+"!" : x) } # ["a", "b!", "c"]
Das Weglassen des "leave alone" Fall natürlich, wenn nicht genug:
new_a = old_a.map { |x| x+"!" if x=="b" } # [nil, "b!", nil]
Was ich möchte, ist so etwas wie folgt aus:
new_a = old_a.map_modifying_only_elements_where (Proc.new {|x| x == "b"})
do |y|
y + "!"
end
# ["a", "b!", "c"]
Gibt es eine schöne Möglichkeit, dies in Ruby zu tun (oder vielleicht hat Rails eine Art bequeme Methode, die ich noch nicht gefunden haben)?
Vielen Dank allen für die Beantwortung. Während Sie hat mich überzeugt, gemeinsam, dass es am besten ist nur map
mit dem ternären Operator zu verwenden, geschrieben einige von euch sehr interessante Antworten!
Lösung
Ich bin damit einverstanden, dass die Karte Aussage ist gut, wie es ist. Es ist klar und einfach ,, und würde einfach für jedermann zu halten.
Wenn Sie etwas komplexer, wie etwa das?
module Enumerable
def enum_filter(&filter)
FilteredEnumerator.new(self, &filter)
end
alias :on :enum_filter
class FilteredEnumerator
include Enumerable
def initialize(enum, &filter)
@enum, @filter = enum, filter
if enum.respond_to?(:map!)
def self.map!
@enum.map! { |elt| @filter[elt] ? yield(elt) : elt }
end
end
end
def each
@enum.each { |elt| yield(elt) if @filter[elt] }
end
def each_with_index
@enum.each_with_index { |elt,index| yield(elt, index) if @filter[elt] }
end
def map
@enum.map { |elt| @filter[elt] ? yield(elt) : elt }
end
alias :and :enum_filter
def or
FilteredEnumerator.new(@enum) { |elt| @filter[elt] || yield(elt) }
end
end
end
%w{ a b c }.on { |x| x == 'b' }.map { |x| x + "!" } #=> [ 'a', 'b!', 'c' ]
require 'set'
Set.new(%w{ He likes dogs}).on { |x| x.length % 2 == 0 }.map! { |x| x.reverse } #=> #<Set: {"likes", "eH", "sgod"}>
('a'..'z').on { |x| x[0] % 6 == 0 }.or { |x| 'aeiouy'[x] }.to_a.join #=> "aefiloruxy"
Andere Tipps
Da Arrays Zeiger sind, diese auch funktioniert:
a = ["hello", "to", "you", "dude"]
a.select {|i| i.length <= 3 }.each {|i| i << "!" }
puts a.inspect
# => ["hello", "to!", "you!", "dude"]
In der Schleife, stellen Sie sicher, dass Sie eine Methode verwenden, die das Objekt ändert, anstatt ein neues Objekt zu erzeugen. Z.B. upcase!
im Vergleich zu upcase
.
Die genaue Vorgehensweise hängt davon ab, was genau Sie versuchen zu erreichen. Es ist schwer, eine eindeutige Antwort mit foo-bar Beispielen nageln.
old_a.map! { |a| a == "b" ? a + "!" : a }
gibt
=> ["a", "b!", "c"]
map!
modifiziert den Empfänger an Ort und Stelle, so old_a
ist nun, dass zurückgegebene Array.
Ihre map
Lösung ist die beste. Ich bin mir nicht sicher, warum Sie denken, map_modifying_only_elements_where ist irgendwie besser. Mit map
ist sauberer, prägnant, und erfordert nicht mehrere Blöcke.
Motto:
["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }
In dem obigen Code, beginnen Sie mit [] "kumulativ". Wie Sie (das Array in unserem Fall [ „a“, „b“, „c“]) durch eine Enumerator aufzählen, kumulativ sowie „der aktuelle“ Einzelteil zu unserem Block übergeben bekommen (| kumulativ, i |) und das Ergebnis der Ausführung unseres Blocks ist kumulativ zugeordnet. Was ich oben tun ist kumulativ unverändert bleiben, wenn das Element nicht „b“ und fügen Sie „b“ kumulative Array und es zurück, wenn es sich um ein b ist.
Es gibt eine Antwort darüber verwendet select
, was der einfachste Weg (und zu erinnern), es zu tun ist.
Sie können select
mit map
kombinieren, um zu erreichen, was Sie suchen:
arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
=> ["b!"]
Im Inneren des select
Block geben Sie die Bedingungen für ein Element „ausgewählt“ werden. Dies wird ein Array zurück. Sie können „Karte“ auf der resultierende Array rufen Sie das Ausrufezeichen, um es anzuhängen.
Wenn Sie nicht das alte Array benötigen, ziehe ich Karte! in diesem Fall, weil Sie können die Verwendung! Verfahren zur Darstellung Sie das Array an Ort und Stelle ändern.
self.answers.map!{ |x| (x=="b" ? x+"!" : x) }
Ich ziehe diese über:
new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }
Es ist ein paar Zeilen lang, aber hier ist eine Alternative für die Hölle von ihr:
oa = %w| a b c |
na = oa.partition { |a| a == 'b' }
na.first.collect! { |a| a+'!' }
na.flatten! #Add .sort! here if you wish
p na
# >> ["b!", "a", "c"]
Die mit ternären collect scheinen am besten in meiner Meinung nach.
Ich habe festgestellt, dass die beste Art und Weise unter Verwendung von tap
dies zu erreichen
arr = [1,2,3,4,5,6]
[].tap do |a|
arr.each { |x| a << x if x%2==0 }
end