Enumable 又叫做枚舉,可以想像城一串資料一個個丟進機器去操作,不同的目的用不同的機器。
以前聽龍哥說過,有人遇到迭代就只會使用#each
方法,戲稱他們是each工程師
。像我在工作中常需要對一串資料做操作,如果每次遇到矩陣只會使用each
,程式碼會看起來很繁瑣。
今天要介紹一些不錯的迭代方法,脫離 Ruby 菜比八的行列。
1.給矩陣元素加上序號(index)
2.Array變成Hash的方法
3.同場加映
這方法可以知道現在迭代到第幾個元素了
detective=['柯南', '小哀', '光彥']
detective.each_with_index do |element, index|
p "第#{index + 1}位偵探: #{element}"
end
輸出結果
"第1位偵探: 柯南"
"第2位偵探: 小哀"
"第3位偵探: 光彥"
map
是對矩陣元素做計算後再放回原位,想知道現在是迭代到第幾個元素,該怎麼做?
直接在.each_with_index
後使用.map
detective.each_with_index.map do |element, index|
"第#{index + 1}位偵探: #{element}"
end
#=> ["第1位偵探: 柯南", "第2位偵探: 小哀", "第3位偵探: 光彥"]
或是.map
搭配.with_index
也有一樣的效果
detective.map.with_index do |element, index|
"第#{index + 1}位偵探: #{element}"
end
#=> ["第1位偵探: 柯南", "第2位偵探: 小哀", "第3位偵探: 光彥"]
假設Rails app 有個Customer
model,有name
跟phone
欄位,想要把name
當key去匹配phone
value
目前資料庫有這兩筆資料
Customer.all
#=> <ActiveRecord::Relation [#<Customer id: 1, name: "龍小台", phone: 919891989, created_at: "2019-09-22 15:07:40", updated_at: "2019-09-22 15:07:40">,
#<Customer id: 2, name: "英小采", phone: 914501450, created_at: "2019-09-22 15:08:19", updated_at: "2019-09-22 15:16:07">]>
先從菜菜each工程師開始
customers = {}
Customer.all.each do |customer|
customers[customer.name] = customer.phone
end
customers #=> {"龍小台"=>919891989, "英小采"=>914501450}
第一行還要先宣告一個空的Hash
,有點繁瑣
Customer.all.inject({}) do |result, customer|
result[customer.name] = customer.phone
result
end
# => {"龍小台"=>919891989, "英小采"=>914501450}
使用inject
先給定初始值(這裏用{}
),每次計算的結果result
會作為下次計算的參數,直到全部迭代完,回傳最後一次的result
但在這裡有一個很不符合DRY的地方,就是在第3行的地方,因為inject每一次重跑時會取用前一次的回傳值當做result,因此最後需要使用result變數回傳,否則依照Ruby回傳值的邏輯會產生錯誤。例如第一次迭代回傳919891989
,是Integer
。下一次迭代變成919891989[customer.name]
就噴錯了。
Customer.all.inject({}) do |result, customer|
result.merge({customer.name => customer.phone})
end
# => {"龍小台"=>919891989, "英小采"=>914501450}
每次迭代產生的新hash都合併進原本的result,就可以一行回傳最新的result
Customer.all.each_with_object({}) do |customer, result|
result[customer[:name]] = customer[:phone]
end
# => {"龍小台"=>919891989, "英小采"=>914501450}
一樣要給原始值,但是變數順序與inject相反,每次迭代後的result放在後方
每一次跑完不取回傳值,而是取原始值被修改後的樣子,不需擔心回傳值的問題
只能使用於object、hash、array等型態,無法用於數字和字串。例如:
[1,2,3].each_with_object(0) {|number, result| result + number}
# => 0
=> 永遠都是原始值,並不會改變
同場加映其他有趣的方法
回傳第一個符合元素的index(第一個元素inex為0),若沒有則回傳nil
如果有給block,則回傳第一個讓block回傳true的元素index
a = [ "a", "b", "c" ]
a.index("b") #=> 1
a.index("z") #=> nil
a.index { |x| x == "b" } #=> 1
回傳第一個讓block回傳true的元素,沒有則回傳nil
沒給block就回傳Enumerator
[1,2].find #=> #<Enumerator: [1, 2]:find>
(1..100).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
(1..100).find { |i| i % 5 == 0 and i % 7 == 0 } #=> 35
今天是壓線,可以體會漫畫家被催稿的壓力了w(゚Д゚)w