在 rails 當中經常會使用 hash,rails 也提供很多方便的 methods
Hash
1
2
3
Hash [ "a" , 100 , "b" , 200 ] #=> {"a"=>100, "b"=>200}
Hash [ [ [ "a" , 100 ] , [ "b" , 200 ] ] ] #=> {"a"=>100, "b"=>200}
Hash [ "a" => 100 , "b" => 200 ] #=> {"a"=>100, "b"=>200}
merge & merge!
回傳新的 hash,後面一樣的 key 則會覆蓋前面的
以後面的為優先
1
2
3
4
5
6
7
8
h1 = { "a" => 100 , "b" => 200 }
h2 = { "b" => 254 , "c" => 300 }
h1 . merge ( h2 ) #=> {"a"=>100, "b"=>254, "c"=>300}
#將重複的做其他處理
h1 . merge ( h2 ){ | key , oldval , newval | newval - oldval }
#=> {"a"=>100, "b"=>54, "c"=>300}
h1 #=> {"a"=>100, "b"=>200}
merge!
直接改變原先的 hash
1
2
3
4
h1 . merge! ( h2 )
#=> {"a"=>100, "b"=>254, "c"=>300}
h1
#=> {"a"=>100, "b"=>254, "c"=>300}
reverse_merge & reverse_merge!
回傳新的 hash,前面一樣的 key 則會覆蓋後面的
通常用在指定hash的預設值
以前面的為優先
1
2
3
4
5
6
7
h1 = { "a" => 100 , "b" => 200 }
h2 = { "b" => 254 , "c" => 300 }
h1 . merge ( h2 )
#=> {"a"=>100, "b"=>254, "c"=>300}
h1 . reverse_merge ( h2 )
#=> {"b"=>200, "c"=>300, "a"=>100}
deep_merge & deep_merge!
在兩個hash的鍵值相同,而值也是個hash的情況下
1
2
3
4
5
6
7
h1 = { :a => { :b => 1 }}
h2 = { :a => { :c => 2 }}
h1 . merge ( h2 )
#=> {:a=>{:c=>2}} error
h1 . deep_merge ( h2 )
#=> {:a=>{:b=>1, :c=>2}}
fetch
即使是 nil, false 也會回傳,只有在空值的時候回傳預設值
1
2
3
4
5
6
7
8
9
10
h = { "a" => 100 , "b" => false , 'c' => nil }
#=> {"a"=>100, "b"=>false, "c"=>nil}
h . fetch ( 'a' , 8 )
#=> 100
h . fetch ( 'b' , 8 )
#=> false
h . fetch ( 'c' , 8 )
#=> nil
h . fetch ( 'd' , 8 )
#=> 8
在處理應該存在的哈希鍵時,使用 fetch。
1
2
3
4
5
6
7
heroes = { batman : 'Bruce Wayne' , superman : 'Clark Kent' }
# bad - if we make a mistake we might not spot it right away
heroes [ :batman ] # => "Bruce Wayne"
heroes [ :supermann ] # => nil
# good - fetch raises a KeyError making the problem obvious
heroes . fetch ( :supermann )
在使用 fetch 時,使用第二個參數設置默認值而不是使用自定義的邏輯。
1
2
3
4
5
6
7
batman = { name : 'Bruce Wayne' , is_evil : false }
# bad - if we just use || operator with falsy value we won't get the expected result
batman [ :is_evil ] || true # => true
# good - fetch work correctly with falsy values
batman . fetch ( :is_evil , true ) # => false
盡量用 fetch 加區塊而不是直接設定默認值。
1
2
3
4
5
6
7
8
batman = { name : 'Bruce Wayne' }
# bad - if we use the default value, we eager evaluate it
# so it can slow the program down if done multiple times
batman . fetch ( :powers , get_batman_powers ) # get_batman_powers is an expensive call
# good - blocks are lazy evaluated, so only triggered in case of KeyError exception
batman . fetch ( :powers ) { get_batman_powers }
except & except!
通常用在確保某些欄位不要被傳進來的參數修改到
1
2
h1 = { a : 100 , b : 200 }
h1 . except ( :a )
symbolize_keys & symbolize_keys!
回傳新的 hash,key 值轉成 symbol
通常用在確保 key 的一致性
1
2
3
4
hash = { 'name' => 'Rob' , 'age' => '28' }
hash . symbolize_keys
# => {:name=>"Rob", :age=>"28"}
stringify_keys & stringify_keys!
回傳新的 hash,key 值轉成 string
alias to_options
& to_options!
通常用在確保 key 的一致性
1
2
3
4
5
6
7
8
hash = { name : 'Rob' , age : '28' }
hash . stringify_keys
#=> { "name" => "Rob", "age" => "28" }
#若有衝突已後面為優先
{ "a" => 1 , :a => 2 } . stringify_keys
#=> {"a"=>2}
slice & slice!
有 !
行為會不太一樣,要注意
1
2
3
4
5
6
7
8
9
10
11
h1 = { "a" => 100 , "b" => 200 }
h1 . slice ( "a" )
#=> {"a"=>100}
h1
#=> {"a"=>100, "b"=>200}
h1 . slice! ( "a" )
#=> {"b"=>200}
h1
#=> {"a"=>100}
extract!
將需要的值提取出來,成為新的 hash
1
2
3
4
5
h1 = { "a" => 100 , "b" => 200 }
h1 . extract! ( "a" )
#=> {"a"=>100}
h1
#=> {"b"=>200}
to_query
Alias for Hash#to_query
1
2
3
4
5
{ name : 'David' , nationality : 'Danish' } . to_query
#=> "name=David&nationality=Danish"
{ name : 'David' , nationality : 'Danish' } . to_query ( 'user' )
# => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
values_at
1
2
3
4
5
6
# bad
email = data [ 'email' ]
nickname = data [ 'nickname' ]
# good
email , username = data . values_at ( 'email' , 'nickname' )
other
attributes
將物件轉成 hash
1
2
3
4
5
foo = Book . first
#=> #<Book id: 22, name: "book", desc: "desc", created_at: "xxx", updated_at: "xxx">
foo . attributes
#=> {"id"=>22, "name"=>"book", "desc"=>"desc", "star"=>1, "created_at"=>xxx, "updated_at"=>xxx}
HashWithIndifferentAccess
最後是在 rails 中有這個 method 可以很方便地讓你不管是用 string 或是 symbol 都可以拿到值,在 params 中也是因為這個方法,因此兩種都取得到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
a = Hash . new
# => {}
a [ 'hi' ] = 123
# => 123
a [ 'hi' ]
# => 123
a [ :hi ]
# => nil
b = HashWithIndifferentAccess . new
# => {}
b [ 'hello' ] = 321
# => 321
b [ 'hello' ]
#=> 321
b [ :hello ]
# => 321
aa = a . with_indifferent_access
# => {"hi"=>123}
aa [ 'hi' ]
# => 123
aa [ :hi ]
# => 123
官方文件:
Ruby Doc Hash
Hash apidock
參考文件:
ActiveSupport - 工具函式庫