Ruby面試精選30題 - Day20 Ruby的Enumerable列舉

30天挑戰已經進入2/3的里程盃了。可喜可賀,恭喜老爺,賀喜夫人!


重點摘要:


Ruby經典面試題目 #20

  • 說明Ruby裡Enumerable的實用之處?
    Why Is Enumerable So Useful?`

列舉(Enumerables)實用之處在於,能夠幫我們方便地走過、搜尋、排序或轉換集合內的元素(traverse, , sort, search, and transform collections)。

而集合(Collection)跟斯斯一樣有三種:陣列Array雜湊Hash範圍Range,在前19天練習經典題目時,我都剛好有碰到、並透過舉例了解如何使用~今天就以陣列為例,來進一步深入了解列舉的各項技能!

1. 在陣列中移動(traverse)

Array#each

[第14天]時我們曾經比較過eachmap / collect。延續昨天的舉例,我們來使用季節作為陣列元素:

season = ["Spring",
"Summer",
"Autumn",
"Winter"]

p season.each

Output:

#<Enumerator: ["Spring", "Summer", "Autumn", "Winter"]:each>

看到了Enumerator這個字眼!

2. 排序陣列中的元素(sort)

Array#sort

.sort 會幫我們按造字母順序重新排序

p season.sort #["Autumn", "Spring", "Summer", "Winter"]
p season #["Spring", "Summer", "Autumn", "Winter"]

.sort! 會幫我們按造字母順序重新排序。加了!驚嘆號的method通常是提醒我們要小心!注意!因為此方法會改寫原本物件裡的值。在這裡的陣列已經被新的排序改寫:

p season.sort! #["Autumn", "Spring", "Summer", "Winter"]
p season #["Autumn", "Spring", "Summer", "Winter"]

(.sort!會造成氣候變遷~季節大亂啊…)

3. 在陣列中搜尋(search)

Ruby提供許多語法幫我們完成搜尋任務。現在就來一一介紹吧!

3.0 Array#find_index(object)

我們可以用find_index搜尋某個元素的index(索引值,從0開始)

p season.find_index("Summer") # 印出index=1

3.1 Array#first

搜尋陣列中的第1個元素,可以用.first方法:

p season.first #"Spring"

當不能確定元素內值的完整資訊時

Question: 我們想要搜尋出季節陣列裡,開頭是S的元素。此題怎解?

3.2 Array#select 加上String Methods

在[第9天]時提過字串有許多的方法,這邊要介紹新的兩種: .include?match()

3.2.1 String#include? + 字元符號

Ans: 陣列方法.select可以幫忙做挑選;而在Block{}裡面,用String#include?做條件判斷,像個過濾網一樣,找出包含大寫'S'開頭的元素:

p season.select {|s| s.include? 'S'}
# ["Spring", "Summer"]
p season.select {|s| s.include? 't'}
# ["Autumn", "Winter"]

3.2.2 String#match()代入正規表達式

正規表達式(Regular Expression,常簡寫為RegExpregexRE)是被用來匹配字串String中,字元組合的模式。

想像我們在用Google大神搜尋資料,最重要的就是輸入關鍵字了。有時候我們會在關鍵字加入+加號、| (OR),或是用-減號剔除篩選字。正規表達具有類似的功能。

同樣的,如果我們想要在進階搜尋的話,就必須使用到正規表達式。

Q2. 我們想要搜尋出季節陣列裡,第二個字母是u的元素。此題怎解?

p season.select {|s| s.match(/^*u/)}
# ["Summer", "Autumn"]

/正規表達符號/放在一組斜線/裡面,以上的例子,我們需要將^開頭的第一個字元設定為任意字元*,第二個字元為u(正規表達規則請見Ruby API說明)。

3.3 Array#select 搭配 String[/RegExp/]

我們也可以省略String Method,直接在在block內用元素s[/加入正規表達/]來篩選符合條件的元素

p season.select{|s| s[/^*u/]}
# ["Summer", "Autumn"]

3.4 Array#grep(/RegExp/) :省略block

同樣解法,且程式碼更少的簡便寫法:

p season.grep(/^sa/)
# []

更多的.grep用法看這裡

4. 將陣列中的元素作轉換(transform)

在實務上,我們常常將集合內的值做轉換,套用到需要的場景,[第14天]時學過的 .map可以在此時派上用場。

4.1 Array#map 不改寫陣列裡的值

北半球的天氣漸漸變冷,我來寫一個溫度陣列temperature,並用.map放入新的零下溫度陣列,但不影響到原本陣列裡的值 :

temperature = [0,1,2,3,4,5,6,7,8,9]
minus_temp = temperature.map{|i| -i}

p temperature.join(", ")   #=> "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"
p minus_temp.join(", ") #=> "0, -1, -2, -3, -4, -5, -6, -7, -8, -9"

4.2 Array#map! 改寫陣列裡的值

加了!驚嘆號的方法又出現了~大家應該可以猜到.map!會改變陣列值:

temperature = [0,1,2,3,4,5,6,7,8,9]
temperature.map!{|x| x -x}  #將"0, -1, -2, -3, -4, -5, -6, -7, -8, -9"與原陣列各個元素相加  
p temperature.join(',')
#=> => "0,0,0,0,0,0,0,0,0,0"

全部變0度啦!

[番外篇] enumerable結合yield

[昨天]講過yield的功用多多,今天來把它和列舉變數結合,再產生一次新火花~:)

class Winter #北半球冬天
  include Enumerable #包含Enumerable Module(否則會出現NomethodError)
  def initialize
    @month = %w[ December January Fabruary]  
  end
  def each # 定義each方法(否則Winter.new.map 會出現 NoMethodError)
    for month in @month do
      yield month
    end
  end
end
p Winter.new.map { |mth| mth.upcase }
# 將月份改成大寫

Output:

["DECEMBER", "JANUARY", "FABRUARY"]

天氣轉涼了,大家要注意保暖唷:)

Ref: