List comprehension in Ruby

2019-01-10 04:57发布

To do the equivalent of Python list comprehensions, I'm doing the following:

some_array.select{|x| x % 2 == 0 }.collect{|x| x * 3}

Is there a better way to do this...perhaps with one method call?

14条回答
干净又极端
2楼-- · 2019-01-10 05:29

Enumerable has a grep method whose first argument can be a predicate proc, and whose optional second argument is a mapping function; so the following works:

some_array.grep(proc {|x| x % 2 == 0}) {|x| x*3}

This isn't as readable as a couple of other suggestions (I like anoiaque's simple select.map or histocrat's comprehend gem), but its strengths are that it's already part of the standard library, and is single-pass and doesn't involve creating temporary intermediate arrays, and doesn't require an out-of-bounds value like nil used in the compact-using suggestions.

查看更多
你好瞎i
3楼-- · 2019-01-10 05:30

If you really want to, you can create an Array#comprehend method like this:

class Array
  def comprehend(&block)
    return self if block.nil?
    self.collect(&block).compact
  end
end

some_array = [1, 2, 3, 4, 5, 6]
new_array = some_array.comprehend {|x| x * 3 if x % 2 == 0}
puts new_array

Prints:

6
12
18

I would probably just do it the way you did though.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-01-10 05:30

How 'bout:

some_array.map {|x| x % 2 == 0 ? x * 3 : nil}.compact

Slightly cleaner, at least to my taste, and according to a quick benchmark test about 15% faster than your version...

查看更多
虎瘦雄心在
5楼-- · 2019-01-10 05:36

Something like this:

def lazy(collection, &blk)
   collection.map{|x| blk.call(x)}.compact
end

Call it:

lazy (1..6){|x| x * 3 if x.even?}

Which returns:

=> [6, 12, 18]
查看更多
SAY GOODBYE
6楼-- · 2019-01-10 05:38

I've just published the comprehend gem to RubyGems, which lets you do this:

require 'comprehend'

some_array.comprehend{ |x| x * 3 if x % 2 == 0 }

It's written in C; the array is only traversed once.

查看更多
小情绪 Triste *
7楼-- · 2019-01-10 05:38
[1, 2, 3, 4, 5, 6].collect{|x| x * 3 if x % 2 == 0}.compact
=> [6, 12, 18]

That works for me. It is also clean. Yes, it's the same as map, but I think collect makes the code more understandable.


select(&:even?).map()

actually looks better, after seeing it below.

查看更多
登录 后发表回答