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!

War es hilfreich?

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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top