質問

Rubyで、特定の要素が変更され、他の要素が変更されないように配列をマッピングする最も表現力のある方法は何ですか?

これは簡単な方法です:

old_a = ["a", "b", "c"]                         # ["a", "b", "c"]
new_a = old_a.map { |x| (x=="b" ? x+"!" : x) }  # ["a", "b!", "c"]

「leave-alone」の省略もちろん、十分でない場合:

new_a = old_a.map { |x| x+"!" if x=="b" }       # [nil, "b!", nil]

私が望むのは次のようなものです:

new_a = old_a.map_modifying_only_elements_where (Proc.new {|x| x == "b"}) 
        do |y|
          y + "!"
        end
# ["a", "b!", "c"]

Rubyでこれを行う良い方法はありますか(または、Railsにはまだ見つけていない便利なメソッドがあります)?


ご返信いただきありがとうございます。あなたは、 map を三項演算子と一緒に使用するのが最善であると私にまとめて納得させましたが、非常に興味深い答えを投稿した人もいます!

役に立ちましたか?

解決

mapステートメントがそのままであることに同意します。それは明確でシンプルで、簡単だろう 誰でも維持できます。

もっと複雑なものが必要な場合、これはどうですか?

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"

他のヒント

配列はポインターであるため、これも機能します:

a = ["hello", "to", "you", "dude"]
a.select {|i| i.length <= 3 }.each {|i| i << "!" }

puts a.inspect
# => ["hello", "to!", "you!", "dude"]

ループでは、新しいオブジェクトを作成するのではなく、オブジェクトを変更するメソッドを使用してください。例えば。 upcase! upcase の比較。

正確な手順は、何を達成しようとしているかによって異なります。 foo-barの例で明確な答えを見つけるのは難しいです。

old_a.map! { |a| a == "b" ? a + "!" : a }

与える

=> ["a", "b!", "c"]

map!はレシーバーをその場で変更するため、 old_a は配列を返します。

あなたの map ソリューションが最適です。 map_modifying_only_elements_whereがどういうわけか良いと思う理由がわかりません。 map を使用すると、より簡潔で簡潔になり、複数のブロックを必要としません。

1つのライナー:

["a", "b", "c"].inject([]) { |cumulative, i| i == "b" ? (cumulative << "#{i}!") : cumulative }

上記のコードでは、[]&quot; cumulative&quot;で始まります。列挙子(この場合、配列["" a ""、 "" b ""、 "c" "])を通じて列挙すると、累積的および「現在」 itemはブロック(| cumulative、i |)に渡され、ブロックの実行結果は累積に割り当てられます。上記のことは、アイテムが「b」でない場合に累積的に変更しないことです。 &quot; b!&quot;を追加します累積配列に追加し、それがbの場合にそれを返します。

select を使用する上記の回答があります。これは、それを行う(そして覚える)最も簡単な方法です。

探しているものを実現するために、 select map と組み合わせることができます。

 arr = ["a", "b", "c"].select { |i| i == "b" }.map { |i| "#{i}!" }
 => ["b!"]

select ブロック内で、「選択」する要素の条件を指定します。これは配列を返します。 &quot; map&quot;を呼び出すことができます。結果の配列に感嘆符を追加します。

古い配列が必要ない場合は、mapをお勧めします!この場合、!配列を変更していることを表すメソッド。

self.answers.map!{ |x| (x=="b" ? x+"!" : x) }

私はこれを好む:

new_map = self.old_map{ |x| (x=="b" ? x+"!" : x) }

数行の長さですが、これに代わる代替手段があります:

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"]

三項のコレクトは私の意見では最高のようです。

これを達成する最良の方法は、 tap

を使用することであることがわかりました。
arr = [1,2,3,4,5,6]
[].tap do |a|
  arr.each { |x| a << x if x%2==0 }
end
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top