在Ruby中,是否有一个Array方法结合了“select”和“map”?
我有一个包含一些string值的Ruby数组。 我需要:
- find匹配某个谓词的所有元素
- 通过转换运行匹配的元素
- 以数组forms返回结果
现在我的解决scheme如下所示:
def example matchingLines = @lines.select{ |line| ... } results = matchingLines.map{ |line| ... } return results.uniq.sort end
是否有一个数组或枚举方法将select和map组合成一个逻辑语句?
我通常使用map
和compact
在一起作为一个后缀, if
我的select标准。 compact
摆脱nils。
jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1} => [3, 3, 3, nil, nil, nil] jruby-1.5.0 > [1,1,1,2,3,4].map{|n| n*3 if n==1}.compact => [3, 3, 3]
你可以使用reduce
,这只需要一个pass:
[1,1,1,2,3,4].reduce([]) { |a, n| a.push(n*3) if n==1; a } => [3, 3, 3]
换句话说,初始化状态是你想要的(在我们的例子中是一个空的列表来填充: []
),然后总是确保返回这个值,修改原始列表中的每个元素(在我们的例子中,修改元素推送到列表)。
这是最有效率的,因为它只能通过一个循环遍历列表( map
+ select
或compact
需要两遍)。
在你的情况下:
def example results = @lines.reduce([]) do |lines, line| lines.push( ...(line) ) if ... lines end return results.uniq.sort end
另一个不同的方式来处理这个是使用新的(相对于这个问题) Enumerator::Lazy
:
def example @lines.lazy .select { |line| line.property == requirement } .map { |line| transforming_method(line) } .uniq .sort end
.lazy
方法返回一个懒惰的枚举器。 在惰性枚举器上调用.select
或.map
返回另一个惰性枚举器。 只有你调用.uniq
它实际上强制枚举器并返回一个数组。 那么有效的做法是将你的.select
和.map
调用合并为一个 – 你只需迭代@lines
就可以同时执行.select
和.map
。
我的直觉是Adam的reduce
方法会快一点,但是我认为这样更具可读性。
这样做的主要结果是没有为每个后续的方法调用创build中间数组对象。 在正常的@lines.select.map
情况下, select
返回一个数组,然后由map
修改,再次返回一个数组。 相比之下,懒惰评估只创build一个数组一次。 当您的初始集合对象很大时,这非常有用。 它也授权你使用无限枚举器 – 例如random_number_generator.lazy.select(&:odd?).take(10)
。
我不确定是否有一个。 Enumerable模块 ,添加select
和map
,不显示一个。
你将被要求通过两个块到select_and_transform
方法,这将是一个有点不直观的恕我直言。
显然,你可以把它们链接在一起,这样更加可读:
transformed_list = lines.select{|line| ...}.map{|line| ... }
如果你有一个select
可以使用case
运算符( ===
), grep
是一个好的select:
p [1,2,'not_a_number',3].grep(Integer){|x| -x } #=> [-1, -2, -3] p ['1','2','not_a_number','3'].grep(/\D/, &:upcase) #=> ["NOT_A_NUMBER"]
如果我们需要更复杂的逻辑,我们可以创buildlambdas:
my_favourite_numbers = [1,4,6] is_a_favourite_number = -> x { my_favourite_numbers.include? x } make_awesome = -> x { "***#{x}***" } my_data = [1,2,3,4] p my_data.grep(is_a_favourite_number, &make_awesome) #=> ["***1***", "***4***"]
def example @lines.select {|line| ... }.map {|line| ... }.uniq.sort end
在Ruby 1.9和1.8.7中,你也可以链接和包装迭代器,只需不传递一个块:
enum.select.map {|bla| ... }
但在这种情况下,这是不可能的,因为select
和map
的块返回值的types不匹配。 这样做更有意义:
enum.inject.with_index {|(acc, el), idx| ... }
AFAICS,你可以做的最好的是第一个例子。
这是一个小例子:
%w[ab 1 2 cd].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end } # => ["a", "b", "c", "d"] %w[ab 1 2 cd].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end } # => ["A", "B", false, false, "C", "D"]
但是你真正想要的是["A", "B", "C", "D"]
。
我认为这种方式更具可读性,因为它将过滤条件和映射值分开,同时保持连接的动作不变:
results = @lines.select { |line| line.should_include? }.map do |line| line.value_to_map end
而且,在你的具体情况下,一起消除result
variables:
def example @lines.select { |line| line.should_include? }.map { |line| line.value_to_map }.uniq.sort end
不,但你可以这样做:
lines.map { |line| do_some_action if check_some_property }.reject(&:nil?)
甚至更好:
lines.inject([]) { |all, line| all << line if check_some_property; all }
简单的答案:
如果您有n条logging,并且您想根据条件select
并map
records.map { |record| record.attribute if condition }.compact
在这里,属性是任何你想从logging和条件你可以把任何检查。
紧凑是冲洗如果条件出来的不必要的零
你应该尝试使用我已经添加了Enumerable#select_map
方法的Enumerable#select_map
。 下面是一个例子:
items = [{version: "1.1"}, {version: nil}, {version: false}] items.select_map{|x| x[:version]} #=> [{version: "1.1"}] # or without enumerable monkey patch Rearmed.select_map(items){|x| x[:version]}
如果你不想创build两个不同的数组,你可以使用compact!
但要小心。
array = [1,1,1,2,3,4] new_array = map{|n| n*3 if n==1} new_array.compact!
有趣的是, compact!
做一个到位删除零。 compact!
的返回值compact!
如果有更改,则是相同的数组,如果没有nil,则为零。
array = [1,1,1,2,3,4] new_array = map{|n| n*3 if n==1}.tap { |array| array.compact! }
将是一个class轮。
这是一个例子。 这与你的问题不一样,但可能是你想要的,或者可以给你的解决scheme提供线索:
def example lines.each do |x| new_value = do_transform(x) if new_value == some_thing return new_value # here jump out example method directly. else next # continue next iterate. end end end