問題描述
如何將單詞的結尾與 Ruby 中的哈希進行比較? (How can I compare the ending of a word with a hash in Ruby?)
我正在嘗試做一些類似字符串分析器的事情,我需要檢索一個單詞的結尾並將其與哈希的鍵進行比較
word = "Test"
ending_hash = {"e" => 1, "st" => 2}
output = 2
我需要輸出為2大小寫,但實際上我不知道結尾的長度是 1 個字符還是 2 個字符。有可能嗎?
參考解法
方法 1:
Initially, assume that you know that word
ends with (at least) one of the keys of ending_hash
. You can then write:
word = "Test"
ending_hash = {"e" => 1, "st" => 2}
ending_hash.find { |k,v| word.end_with?(k) }.last
#=> 2
See Enumerable#find, String#end_with? and Array#last.
The intermediate calculation is as follows:
ending_hash.find { |k,v| word.end_with?(k) }
#=> ["st", 2]
If you are unsure if any of the keys may match the end of the string, write:
ending_hash = {"e" => 1, "f" => 2}
arr = ending_hash.find { |k,v| word.end_with?(k) }
#=> nil
arr.nil? ? nil : arr.last
#=> nil
or better:
ending_hash.find { |k,v| word.end_with?(k) }&.last
#=> nil
Here &
is the Safe Navigation Operator. In a nutshell, if the expression preceding &
returns nil
, the SNO immediately returns nil
for the entire expression, without executing last
.
Even if word
must end with one of the keys, you may want to write it this way so that you can check the return value and raise an exception if it is nil
.
You could alternatively write:
ending_hash.find { |k,v| word.match? /#{k}\z/ }&.last
The regular expression reads, "match the value of k
(#{k}
) at the end of the string (the anchor \z
)".
Note the following:
{"t"=>1, "st"=>2}.find { |k,v| word.end_with?(k) }&.last
#=> 1
{"st"=>1, "t"=>2}.find { |k,v| word.end_with?(k) }&.last
#=> 1
so the order of the keys may matter.
Lastly, as the block variable v
is not used in the block calculation, the block variables would often be written |k,_|
or |k,_v|
, mainly to signal to the reader that only k
is used in the block calculation.
方法 2:
If you know there will be only a small number of lengths of endings, it is much faster to check for all possible lengths, than to check for all endings. (It also makes sense to check them from longest to shortest, in case they overlap, otherwise the shorter will never be matched.)
The lazy one‑liner:
(‑2..‑1).lazy.map { |cut| ending_hash[word[cut..]] }.find(&:itself)
The functional version:
(‑2..‑1).inject(nil) { |a, e| a || ending_hash[word[e..]] }
The blunt but moist version:
ending_hash[word[‑2..]] || ending_hash[word[‑1..]]
(by Gregorio Pompei、Cary Swoveland、Amadan)