19

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

これは簡単な方法です。

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

もちろん、十分でない場合は「放置」のケースを省略します。

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三項演算子を使用するのが最善であるとまとめて確信させてくれましたが、非常に興味深い回答を投稿した人もいました。

4

9 に答える 9

25

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

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の例で明確な答えを見つけるのは難しいです。

于 2009-03-09T10:35:15.537 に答える
9
old_a.map! { |a| a == "b" ? a + "!" : a }

与える

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

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

于 2009-03-05T21:54:12.707 に答える
6

マップステートメントはそのままで良いと思います。それは明確でシンプルであり、誰でも簡単に保守できます。

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

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"
于 2009-03-05T14:59:43.547 に答える
4

あなたのmapソリューションは最高のものです。map_modifying_only_elements_where がどういうわけか優れていると思う理由がわかりません。Usingmapはよりクリーンで簡潔で、複数のブロックを必要としません。

于 2009-03-05T13:50:27.877 に答える
3

一発ギャグ:

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

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

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

あなたが探しているものを達成するためにselect組み合わせることができます:map

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

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

于 2013-03-04T10:18:15.263 に答える