## 映射
Maps 保存從key到value的a對應關系 — key和value都可以是任意對象。key-value 組合被以一種可以按照key的順序高效獲取的方式保存著。
下面是創建map的一些方法, 其中逗號是為了提高可讀性的,它是可選的,解析的時候會被當作空格忽略掉的。
```
(def popsicle-map
(hash-map :red :cherry, :green :apple, :purple :grape))
(def popsicle-map
{:red :cherry, :green :apple, :purple :grape}) ; same as previous
(def popsicle-map
(sorted-map :red :cherry, :green :apple, :purple :grape))
```
Map可以作為它的key的函數,同時如果key是keyword的話,那么key也可以作為map的函數。下面是三種獲取:green所對應的值的方法:
```
(get popsicle-map :green)
(popsicle-map :green)
(:green popsicle-map)
```
`contains?` 方法可以操作 sets 和 maps. 當被用在map上的時候,它返回map是否包含給定的key. `keys` 函數返回map里面的所有的key的集合. `vals` 函數返回map里面所有值的集合. 看例子:
```
(contains? popsicle-map :green) ; -> true
(keys popsicle-map) ; -> (:red :green :purple)
(vals popsicle-map) ; -> (:cherry :apple :grape)
```
`assoc` 函數可以操作 maps 和 vectors. 當被用在map上的時候,它會創建一個新的map, 同時添加任意對新的name-value pair, 如果某個給定的key已經存在了,那么它的值會被更新。看例子:
```
(assoc popsicle-map :green :lime :blue :blueberry)
; -> {:blue :blueberry, :green :lime, :purple :grape, :red :cherry}
```
`dissoc` 創建一個新的map, 同時忽略掉給定的那么些key, 看例子:
```
(dissoc popsicle-map :green :blue) ; -> {:purple :grape, :red :cherry}
```
我們也可以把map看成一個簡單的集合,集合里面的每個元素是一個pair: name-value: `clojure.lang.MapEntry` 對象. 這樣就可以和doseq跟destructuring一起使用了, 它們的作用都是更簡單地來遍歷map, 我們會在后面詳細地介紹這些函數. 下面的這個例子會遍歷 `popsicle-map` 里面的所有元素,把key bind到 `color,` 把value bind到 `flavor。` `name函數返回一個keyword的字符串名字。`
```
(doseq [[color flavor] popsicle-map]
(println (str "The flavor of " (name color)
" popsicles is " (name flavor) ".")))
```
上面的代碼的輸出是這樣的:
```
The flavor of green popsicles is apple.
The flavor of purple popsicles is grape.
The flavor of red popsicles is cherry.
```
`select-keys` 函數接收一個map對象,以及一個key的集合的參數,它返回這個集合里面key在那個集合里面的一個子map。看例子:
```
(select-keys popsicle-map [:red :green :blue]) ; -> {:green :apple, :red :cherry}
```
`conj` 函數添加一個map里面的所有元素到另外一個map里面去。如果目標map里面的key在源map里面也有,那么目標map的值會被更新成源map里面的值。
map里面的值也可以是一個map, 而且這樣嵌套無限層。獲取嵌套的值是非常簡單的。同樣的,更新一個嵌套的值也是很簡單的。
為了證明這個, 我們會創建一個描述人(person)的map。其中內嵌了一個表示人的地址的map,同時還有一個叫做employer的內嵌map。
```
(def person {
:name "Mark Volkmann"
:address {
:street "644 Glen Summit"
:city "St. Charles"
:state "Missouri"
:zip 63304}
:employer {
:name "Object Computing, Inc."
:address {
:street "12140 Woodcrest Executive Drive, Suite 250"
:city "Creve Coeur"
:state "Missouri"
:zip 63141}}})
```
`get-in` 函數、宏 `->` 以及函數 `reduce` 都可以用來獲得內嵌的key. 下面展示了三種獲取這個人的employer的address的city的值的方法:
```
(get-in person [:employer :address :city])
(-> person :employer :address :city) ; explained below
(reduce get person [:employer :address :city]) ; explained below
```
宏 `->` 我們也稱為 “thread” 宏, 它本質上是調用一系列的函數,前一個函數的返回值作為后一個函數的參數. 比如下面兩行代碼的作用是一樣的:
```
(f1 (f2 (f3 x)))
(-> x f3 f2 f1)
```
在名字空間 `clojure.contrib.core` 里面還有個 -?>宏, 它會馬上返回nil, 如果它的調用鏈上的任何一個函數返回nil (short-circiut)。這會避免拋出 `NullPointerException` 異常。
`reduce` 函數接收一個需要兩個參數的函數, 一個可選的value以及一個集合。它會以value以及集合的第一個元素作為參數來調用給定的函數(如果指定了value的話), 要么以集合的第一個元素以及第二個元素為參數來調用給定的函數(如果沒有指定value的話)。接著就以這個返回值以及集合里面的下一個元素為參數來調用給定的函數,知道集合里面的元素都被計算了 — 最后返回一個值. 這個函數與ruby里面的 `inject` 以及Haskell里面的 `foldl` 作用是一樣的。
`assoc-in` 函數可以用來修改一個內嵌的key的值。看下面的例子把person的employer->address->city修改成Clayton了。
```
(assoc-in person [:employer :address :city] "Clayton")
```
`update-in` 函數也是用來更新給定的內嵌的key對應的值,只是這個新值是通過一個給定的函數來計算出來。下面的例子里面會把person的employer->address->zip改成舊的zip + “-1234″。看例子:
```
(update-in person [:employer :address :zip] str "-1234") ; using the str function
```