我有一个包含一些字符串值的Ruby数组。我需要:
现在我的解决方案如下:
def example
matchingLines = @lines.select{ |line| ... }
results = matchingLines.map{ |line| ... }
return results.uniq.sort
end
是否有一个Array或Enumerable方法将select和map组合成一个逻辑语句?
答案 0 :(得分:98)
我通常将map
和compact
以及我的选择标准一起用作后缀if
。 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]
答案 1 :(得分:51)
您可以使用reduce
,只需要一次传递:
[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
答案 2 :(得分:15)
另一种接近此方法的不同方法是使用新的(相对于此问题)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
方法会更快一点,但我认为这更具可读性。
这样做的主要结果是没有为每个后续方法调用创建中间数组对象。在正常@lines.select.map
情况下,select
返回一个数组,然后由map
修改,再次返回一个数组。相比之下,延迟评估仅创建一次数组。当您的初始集合对象很大时,这很有用。它还使您能够使用无限的枚举器 - 例如random_number_generator.lazy.select(&:odd?).take(10)
。
答案 3 :(得分:10)
如果您有select
可以使用case
运算符(===
),grep
是一个不错的选择:
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"]
如果我们需要更复杂的逻辑,我们可以创建lambdas:
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***"]
答案 4 :(得分:8)
我不确定有没有。添加select
和map
的{{3}}未显示一个。{3}}。
你需要传递两个块到select_and_transform
方法,这有点不直观恕我直言。
显然,您可以将它们链接在一起,这更具可读性:
transformed_list = lines.select{|line| ...}.map{|line| ... }
答案 5 :(得分:2)
不,但你可以这样做:
lines.map { |line| do_some_action if check_some_property }.reject(&:nil?)
甚至更好:
lines.inject([]) { |all, line| all << line if check_some_property; all }
答案 6 :(得分:2)
我认为这种方式更具可读性,因为拆分过滤条件和映射值,同时清楚表明操作已连接:
results = @lines.select { |line|
line.should_include?
}.map do |line|
line.value_to_map
end
并且,在您的具体情况下,一起消除result
变量:
def example
@lines.select { |line|
line.should_include?
}.map { |line|
line.value_to_map
}.uniq.sort
end
答案 7 :(得分:1)
def example
@lines.select {|line| ... }.map {|line| ... }.uniq.sort
end
在Ruby 1.9和1.8.7中,您也可以通过简单地不将块传递给它来链接和包装迭代器:
enum.select.map {|bla| ... }
但在这种情况下实际上并不可能,因为select
和map
的块返回值的类型不匹配。对于这样的事情更有意义:
enum.inject.with_index {|(acc, el), idx| ... }
AFAICS,你能做的最好的就是第一个例子。
这是一个小例子:
%w[a b 1 2 c d].map.select {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["a", "b", "c", "d"]
%w[a b 1 2 c d].select.map {|e| if /[0-9]/ =~ e then false else e.upcase end }
# => ["A", "B", false, false, "C", "D"]
但真正想要的是["A", "B", "C", "D"]
。
答案 8 :(得分:1)
简单回答:
如果您有n条记录,并且您希望根据条件select
和map
records.map { |record| record.attribute if condition }.compact
在这里,属性是您想要的记录和条件,您可以进行任何检查。
compact是为了清除那些因条件
而产生的不必要的nil答案 9 :(得分:1)
您应该尝试使用我已添加方法Enumerable#select_map
的库Rearmed Ruby。下面是一个例子:
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]}
答案 10 :(得分:1)
Ruby 2.7 +
现在有!
Ruby 2.7为此引入了filter_map
。它是惯用语言和高性能,我希望它很快会成为常态。
例如:
numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
希望对某人有用!
答案 11 :(得分:0)
如果您不想创建两个不同的数组,可以使用compact!
但请注意它。
array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}
new_array.compact!
有趣的是,compact!
会删除nil。如果有更改,compact!
的返回值是相同的数组,但如果没有nils则返回nil。
array = [1,1,1,2,3,4]
new_array = map{|n| n*3 if n==1}.tap { |array| array.compact! }
将是一个班轮。
答案 12 :(得分:-1)
这是一个例子。它与您的问题不同,但可能是您想要的,或者可以为您的解决方案提供线索:
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