2009-03-05 9 views
17

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を三項演算子で使うのが最善であると確信していましたが、あなたの中には非常に興味深い答えが載っていました!

+0

#mapの物はそのままですね。 ;-) –

+0

ええ、私は同意します。あなたがそれをもっと好きになるならば、あなたは括約を取り出すことができます! –

+0

閉鎖? FTL。私のポスト= Pを見てください。P –

答えて

6

私は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" 
+16

1つの行を30に変えました。私はあなたのスタイルが好きです。 – Pesto

+0

私は*しました*条件付きのw /を地図に貼り付けるのが最高だったと言っています:) – rampion

3

お客様のmapソリューションが最適です。なぜあなたはmap_modifying_only_elements_whereが何とか良いと思います。 mapを使用すると、よりクリーンで、より簡潔で、複数のブロックを必要としません。

1

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

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

私が上でこれを好む:

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

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

map!を与えるのでold_aは、今では返される配列である、場所に受信機を変更します。

1

数行長いのですが、ここでの地獄のための代替です:

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

ザ・三項で収集し、私の意見では最高のようです。配列はポインタであるため、

23

、これも動作します:

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

1

私はこれを実現する最良の方法はtap

arr = [1,2,3,4,5,6] 
[].tap do |a| 
    arr.each { |x| a << x if x%2==0 } 
end 
2

ワンライナーを使用していることを発見しました:上記のコードで

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

は、あなたが "累積" []で始まります。列挙子(この例では["a"、 "b"、 "c"])を列挙すると、累積と "現在"の項目がブロック(| cumulative、i |)に渡され、ブロックの実行結果が累積に割り当てられます。私が上記のことは、アイテムが "b"でなく "b!"累積配列に渡し、それがaのときに返す。

上記の答えはselectを使用しています。これは最も簡単な方法です(覚えておいてください)。

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

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

selectブロックの内部では、「選択」される要素のための条件を指定します。これは配列を返します。結果の配列に "map"を呼び出して感嘆符を付け加えることができます。

関連する問題