

例えば、ある配列で、その要素が3の倍数のときのみ、その要素を掛け合わせた配列を新たに作りたいとします。具体的には`[3, 5,
9]`という配列が与えられた場合、期待している結果の配列が`[9, 81]`であるとします。このような判定を実現するためには、下のようなコードで出来ます:

[code]

    [3, 5, 9].map { |x| x * x if x % 3 == 0 }.select { |x| !!x }
    
[/code]

あるいは:

[code]

    [3, 5, 9].select { |x| x % 3 == 0 }.map { |x| x * x }
    
[/code]

といったように。

確かに上のように書くことは可能なのですが、個人的には、`map`の中で、帰ってくる結果が`nil`であるならば、それを配列の要素に含みたくないことを、`select`を使わずに表現したいと思っています。想定しているのは、以下の使い方に近い書き方です:

[code]

    [3, 5, 9].map { |x| x * x if x % 3 == 0 }
    
[/code]

もちろん、このままだと`nil`の要素も含まれてしまうので、上のような希望を達成することが出来ません。上記のような希望を実現できるメソッド、あるいは方法がもしあれば教えてください。


CC BY-SA 3.0
2015-04-16T05:09:31.447
9217
2015-04-21T01:00:52.603
2015-04-16T05:36:17.713
4114
939
question
ruby
nilが結果として帰ってきた場合、その要素は最初から含めないようにするmapのようなメソッド


`map` は一対一対応の写像なので、その中で要素数を減らすのは難しいと思います。

[code]

    [3, 5, 9].map { |x| x * x if x % 3 == 0 }.compact # 写像して nil 削除
    
    [3, 5, 9].inject([]) { |ary, x| ary << x * x if x % 3 == 0; ary } # 畳み込み
    
[/code]

とかいかがでしょう（他にもあるでしょうけど）。


CC BY-SA 3.0
2015-04-16T05:18:00.740
9219
2015-04-16T05:34:47.983
2015-04-16T05:34:47.983
4114
4114
9217
answer


そういうメソッドが欲しいのであればEnumerableモジュールに定義してしまえば作ることはできます。

[code]

    module Enumerable
      def map_without_nil
        self.inject([]) do |arr, item|
          ret = yield item
          arr << ret unless ret.nil?
          arr
        end
      end
    end
    
    [3, 5, 9].map_without_nil {|i| i * i if i %  3 == 0} # => [9, 81]
    
[/code]

ただし、この程度の話だと`select`を使うのより明らかに可読性は低下するので、おすすめはしません。


CC BY-SA 3.0
2015-04-17T04:29:05.323
9249
2015-04-17T07:33:03.360
2015-04-17T07:33:03.360
4114
5793
9217
answer


[singleton method Enumerator::Lazy.new](http://docs.ruby-
lang.org/ja/2.2.0/method/Enumerator=3a=3aLazy/s/new.html)の例で挙げられている`filter_map`を定義するのはいかがでしょうか。

[code]

    module Enumerable
      def filter_map(&block)
        map(&block).compact
      end
    end
    
    class Enumerator::Lazy
      def filter_map
        Lazy.new(self) do |yielder, *values|
          result = yield *values
          yielder << result if result
        end
      end
    end
    
    (1..Float::INFINITY).lazy.filter_map{|i| i*i if i.even?}.first(5)
        # => [4, 16, 36, 64, 100]
    
[/code]

ちなみに、この質問のようなメソッドが欲しいというFeature RequestがRubyのバグトラッキングシステムに出されています。  
いいメソッドの名前があれば採用されてRubyに入って使えるようになると思うので、issueにコメントしてみるといいのではないでしょうか。

[Feature #5663: Combined map/select method - Ruby trunk - Ruby Issue Tracking
System](https://bugs.ruby-lang.org/issues/5663)


CC BY-SA 3.0
2015-04-19T06:19:58.380
9302
2015-04-19T06:19:58.380
2599
9217
answer

[code]

    [3, 5, 9].map { |x| x * x if x % 3 == 0 }.compact
    
[/code]

これでは駄目でしょうか


CC BY-SA 3.0
2015-04-21T01:00:52.603
9363
2015-04-21T01:00:52.603
9403
9217
answer