提升您的 Ruby 技能:使用数组 each map flat_map select detect rejection partition count with_index chaining 您成功了!!!

2025-05-25

提升你的 Ruby 技能:使用数组

each

map

flat_map

select

detect

reject

partition

count

with_index

chaining

你成功了!

我刚开始工作的时候,和一位高级工程师一起工作,他简直就是数组处理方面的高手。他教会了我所有关于数组处理的最佳技巧,让我能够写出简洁干净的代码。以下是我认为最有用的方法,我觉得从一开始就应该把它们添加到你的 Ruby 工具箱中。

如果您想直接跳到代码而不看解释,请查看底部的备忘单!

each

在深入研究上述一些更高级的方法之前,我们先从最基础的方法开始。each.each会为数组中的每个给定元素调用一次代码块。执行完成后,它会返回原始数组。最后一部分非常关键,但很容易忘记。即使是我们这些使用 Ruby 已有一段时间的人,有时也会忘记它。这里有一个例子。

result = [1, 2, 3].each do |number|
  puts 'hi'
end
Enter fullscreen mode Exit fullscreen mode

该代码在控制台中运行时将产生以下结果。注意:在下面以及后续示例中,irb 仅表示我在 Ruby 控制台中。

irb:> result = [1, 2, 3].each do |number|
>   puts "hi #{number}"
> end
hi 1
hi 2
hi 3
=> [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

对于数组中的每个数字,我们都打印“hi”加上该数字。然后,遍历完整个数组后,返回原始数组。

请记住,我使用的是do/end上面的块符号,但您也可以使用如下所示的块的括号语法。

irb:> result = [1, 2, 3].each{|number| puts "hi #{number}"}
hi 1
hi 2
hi 3
=> [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

如您所见,无论使用哪种语法,结果都是一样的。我将do/end在本指南中继续使用该语法,因为我认为它使代码和逻辑更容易理解。话虽如此,所有这些方法也适用于括号语法。

map

早期,当我刚接触 Ruby 时,每次我想要构建一个数组时,我都会做这样的事情:

result = []
[1, 2, 3, 4].each do |number|
  result << number + 2
end
# result = [3, 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

我很快就知道了有一个更好的方法,那就是使用map. map,它会返回一个新数组,其中包含对原始数组中每个元素执行一次代码块的结果。以下是一个例子:

result = [1, 2, 3, 4].map do |number|
  number + 2
end
# result = [3, 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

我们将原始数组中的每个数字加 2,然后将结果组合成一个新数组并返回。现在我们的代码更加简洁紧凑了。

flat_map

map非常适合收集一组结果,但是当你需要映射嵌套数组时会发生什么呢?这时就flat_map派上用场了。如果你发现自己有一组嵌套数组,那么你可能需要检查一下flat_map。例如,假设你有这样的代码,其中包含几个嵌套数组。

result = []
[1, 2, 3].each do |number|
  ['a', 'b', 'c'].each do |letter|
    result << "#{number}:#{letter}"
  end
end
# result = ["1:a", "1:b", "1:c", "2:a", "2:b", "2:c", "3:a", "3:b", "3:c"]
Enter fullscreen mode Exit fullscreen mode

我们得到了一个单层数组,这正是我们想要的,但是如何让它更紧凑呢?让我们尝试使用map

result = [1, 2, 3].map do |number|
  ['a', 'b', 'c'].map do |letter|
    "#{number}:#{letter}"
  end
end
# result = [["1:a", "1:b", "1:c"], ["2:a", "2:b", "2:c"], ["3:a", "3:b", "3:c"]]
Enter fullscreen mode Exit fullscreen mode

嗯,这不太符合我们的要求。我们想要一个扁平的单层数组,并且map正在创建一个嵌套数组。为了扁平化这个嵌套数组,我们可以使用flat_map

result = [1,2,3].flat_map do |number|
  ['a', 'b', 'c'].map do |letter|
    "#{number}:#{letter}"
  end
end
# result = ["1:a", "1:b", "1:c", "2:a", "2:b", "2:c", "3:a", "3:b", "3:c"]
Enter fullscreen mode Exit fullscreen mode

flat_map工作原理与 类似,map它将代码块的结果收集到一个数组中,但额外的好处是,它会将其展平。其底层flat_map是将所有内部数组连接flat_map成一个数组。使用会返回我们想要的单层数组。

select

与示例类似map,当我刚开始时,如果我想有条件地从数组中选择元素,例如,选择所有偶数,我会执行以下操作:

result = []
[1, 2, 3, 4].each do |number|
  result << number if number.even?
end
# result = [2, 4]
Enter fullscreen mode Exit fullscreen mode

它可以工作,但还有一种更简洁的方法,那就是使用select.select返回一个数组,其中包含所有给定代码块返回真值的元素。这意味着我们可以像这样重写上面的代码块!

result = [1, 2, 3, 4].select do |number|
  number.even?
end
# result = [2,4]
Enter fullscreen mode Exit fullscreen mode

detect

现在我们要更进一步。如果您不想要从数组中返回所有偶数,而只想要找到的第一个偶数,该怎么办?为此,您可以使用detect. detect,它将返回代码块求值结果为 true 的第一个条目。因此,如果我们运行与上面类似的代码块,但将 替换selectdetect,您会看到我们只返回第一个偶数。

result = [1, 2, 3, 4].detect do |number|
  number.even?
end
# result = 2
Enter fullscreen mode Exit fullscreen mode

这里要注意的一件重要事情是,我们现在返回一个数字(我们的条目)而不是一个数组。

但是如果我们的代码块永远无法计算为 true 会发生什么?如果数组中没有偶数怎么办?在这种情况下,detect将返回 nil。

result = [1, 3, 5, 7].detect do |number|
  number.even?
end
# result = nil
Enter fullscreen mode Exit fullscreen mode

总而言之,detect将返回您的块评估为 true 的第一个条目,或者,如果没有条目对您的块评估为 true,它将返回 nil。

reject

现在我们来看看 的逆运算select,即rejectreject它将返回所有代码块求值为 FALSE 的条目。因此,不要这样做:

result = []
[1, 2, 3, 4].each do |number|
  result << number if !number.even?
end
# result = [1, 3]
Enter fullscreen mode Exit fullscreen mode

我们可以简化上面的代码并改为执行以下操作:

result = [1, 2, 3, 4].reject do |number|
  number.even?
end
# result = [1, 3]
Enter fullscreen mode Exit fullscreen mode

这次我们将返回每个不为偶数的数字,因此这些数字number.even?将返回 false。

partition

select我们刚刚了解了在 Ruby 中使用和过滤数组的两种方法reject。但是,如果你想直接将单个数组分成两个数组,一个用于存储偶数,一个用于存储奇数,该怎么办?一种方法是这样做:

even = [1, 2, 3, 4].select do |number|
  number.even?
end
# even = [2, 4]
odd = [1, 2, 3, 4].reject do |number|
  number.even?
end
# odd = [1, 3]
Enter fullscreen mode Exit fullscreen mode

但是,还有更好的方法,你可以使用partition!请坐稳了。partition它将返回两个数组,第一个包含原始数组中代码块求值为 true 的元素,第二个包含其余元素。这意味着我们可以将上面写的内容简化为:

result = [1, 2, 3, 4].partition do |number|
  number.even?
end
# result = [[2, 4], [1, 3]]
Enter fullscreen mode Exit fullscreen mode

如你所见,partition它将返回两个数组,一个包含偶数,一个包含奇数。如果我们要为evenodd变量赋值,只需

even = result.first
odd = result.last
Enter fullscreen mode Exit fullscreen mode

不过,你可能已经猜到了,还有更好的办法!我们可以result完全去掉那个变量,然后像这样写:

even, odd = [1, 2, 3, 4].partition do |number|
  number.even?
end
# even = [2, 4] and odd = [1, 3]
Enter fullscreen mode Exit fullscreen mode

此语法会自动将第一个数组赋值给 ,even将第二个数组赋值给odd。处理嵌套数组时,你可以随时使用此数组赋值语法。以下是如何拆分 3 个数组的示例。

irb:> a, b, c = [[1], [2], [3]]
=> [[1], [2], [3]]
irb:> a
=> [1]
irb:> b
=> [2]
irb:> c
=> [3]
Enter fullscreen mode Exit fullscreen mode

count

count大部分内容都是不言自明的,默认情况下,它会计算数组中元素的数量。

irb:> [1, 1, 2, 2, 3, 3].count
=> 6
Enter fullscreen mode Exit fullscreen mode

但是,你知道它还能做更多吗?首先,它可以传递count一个参数。如果你传递count一个参数,它会计算该参数在数组中出现的次数。

irb:> [1, 1, 2, 2, 3, 3].count(1)
=> 2
irb:> ['a', 'a', 'b', 'c'].count('c')
=> 1
Enter fullscreen mode Exit fullscreen mode

您还可以传递count一个块!😲当传递一个块时,count将返回该块评估为真的条目数的计数。

irb:> [1, 1, 2, 2, 3, 3].count do |number|
  number.odd?
end
=> 4
Enter fullscreen mode Exit fullscreen mode

我们计算了数组中的每个奇数,返回的结果是 4。

with_index

最后,我想谈谈如何使用索引遍历数组。通常,当我们想要跟踪元素在数组中的位置时,我们会这样做。

irb:> index = 0
irb:> ['a', 'b', 'c'].each do |letter|
  puts index
  index += 1
end
0
1
2
Enter fullscreen mode Exit fullscreen mode

不过,还有更好的方法!你可以使用with_indexwith each,或者我上面列出的任何方法来帮助你追踪数组中的位置。以下是一些使用方法的示例。(记住:数组索引从 0 开始😃)

irb:> ['a', 'b', 'c'].each.with_index do |letter, index|
  puts index
end
0
1
2
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们只是迭代我们的数组并打印出每个元素的索引。

result = ['a', 'b', 'c'].map.with_index do |letter, index|
  "#{letter}:#{index}"
end
# result = ["a:0", "b:1", "c:2"]
Enter fullscreen mode Exit fullscreen mode

在这个例子中,我们将索引与数组中的字母结合起来,使用该map方法形成一个新数组。

result = ['a', 'b', 'c'].select.with_index do |letter, index|
  index == 2
end
# result = ["c"]
Enter fullscreen mode Exit fullscreen mode

这个例子稍微有点棘手。这里我们使用索引来选择数组中索引为 2 的元素。在本例中,这个元素是“c”。

chaining

最后我想分享的一点是,上面所有返回数组的方法(除了count和 之外detect)都可以串联起来。在这些例子中,我将使用括号表示法,因为我认为从左到右而不是从上到下阅读串联方法更容易。

例如,您可以这样做:

result = [1, 2, 3, 4].map{|n| n + 2}.select{|n| n.even?}.reject{|n| n == 6}.map{|n| "hi #{n}"} 
# result = ["hi 4"]
Enter fullscreen mode Exit fullscreen mode

让我们根据上面学到的知识来分析一下这里发生的事情。1
map将向每个数组元素添加 2 并返回[3, 4, 5, 6]
2)select将仅从该数组中选择偶数并返回[4, 6]
3)reject将删除任何等于 6 的数字,剩下[4]
4)我们的最终map将在 4 前面加上“hi”并返回["hi 4"]

你成功了!


恭喜,你完成了!希望这些数组方法在你编写 Ruby 代码时能派上用场。如果还有什么不清楚的地方,请在评论区留言。这是我第一次写教程,欢迎大家提出任何反馈🤗


如果您想要所有这些代码示例而不需要冗长的解释,请查看@lukewduncan慷慨整理的这份备忘单!

文章来源:https://dev.to/molly/level-up-your-ruby-skillz-working-with-arrays-hnn
PREV
从 Memcache 切换到 Redis 以及有关缓存的一些提示为什么我们要切换移动键的策略全部或全部翻转开关缓存 Gotcha 快乐缓存!
NEXT
如果你是新开发者,就从这里开始吧!入门指南:支持与指导、语言无关的初学者指南、实话实说,祝你好运!🍀