第十章 Scala 容器基礎(十四):使用map把一個集合轉化爲另外一個

Problem

    像上一節同樣,你想把一個集合的每一個元素經過某種算法變換後生成一個新的集合
es6

Solution

    咱們要調用集合的map方法,而後傳給它一個函數、匿名函數或者方法來對每個集合元素進行變換,而不是for/yield。下面這個例子中咱們能夠看到,咱們把一組字符串的首字母變爲大寫:算法

scala> val helpers = Vector("adam", "kim", "melissa")
helpers: scala.collection.immutable.Vector[String] = Vector(adam, kim, melissa)

scala> helpers.map(e => e.capitalize)
res26: scala.collection.immutable.Vector[String] = Vector(Adam, Kim, Melissa)

scala> helpers.map(_.capitalize)
res1: scala.collection.immutable.Vector[String] = Vector(Adam, Kim, Melissa)

    在下面這個例子中,咱們把一個字符串集合轉化成了一個整形集合:api

scala> val names = Array("Fred", "Joe", "Jonathan")
names: Array[String] = Array(Fred, Joe, Jonathan)

scala> val lengths = names.map(_.length)
lengths: Array[Int] = Array(4, 3, 8)

    一樣地,map方法能夠把一個集合轉化爲一個xml元素:數組

scala> val nieces = List("Aleka", "Christina", "Molly")
nieces: List[String] = List(Aleka, Christina, Molly)

scala> val elems = nieces.map(niece => <li>{niece}</li>)
elems: List[scala.xml.Elem] = List(<li>Aleka</li>, <li>Christina</li>, <li>Molly</li>)

    咱們可使用相似的方法,把一個集合轉化爲ul:app

scala> val ul = <ul>{nieces.map(niece => <li>{niece}</li>)}</ul>
ul: scala.xml.Elem = <ul><li>Aleka</li><li>Christina</li><li>Molly</li></ul>

    被傳入map方法的函數能夠是任意複雜度的,固然函數的負責度這是根據你的需求來制定的。在Discussion中你會看到如何在map中使用一個多行匿名函數。當你的算法足夠複雜,直到匿名函數沒法知足你的需求的時候,你能夠先定義一個函數,而後把這個函數傳給map方法。函數

scala> def plusOne(c: Char): Char = (c.toByte+1).toChar
plusOne: (c: Char)Char

scala> "HAL".map(plusOne)
res2: String = IBM

    當你想定義一個可以傳入map方法的函數的時候,這個函數必須只有一個和集合元素同類型的參數。在上面這個例子中,plusOne被定義爲接受一個char,由於String世界上就是一個char的集合元素。函數的返回值能夠是任何你想要的。實際上names.map(_.length)就是這麼一個輸入String,輸出Int的函數。ui

    和for/yield結構不相同的地方是map方法能夠造成一個方法調用鏈。也就是你能夠在map方法後直接對map返回的集合進行其它操做活着把map加在一個返回集合的方法的後面。好比,你能夠把一個字符串根據某中規則切分紅一個字符串數組,而後再去掉字符串兩邊的空格。
es5

scala> val s = " eggs, milk, butter, Coco Puffs "
s: String = " eggs, milk, butter, Coco Puffs "

scala> val items = s.split(",").map(_.trim)
items: Array[String] = Array(eggs, milk, butter, Coco Puffs)

Discussion

    對於簡單一點的狀況來講,使用map和使用for/yield是同樣:spa

scala> val people = List("adam", "kim", "melissa")
people: List[String] = List(adam, kim, melissa)

scala> val caps1 = people.map(_.capitalize)
caps1: List[String] = List(Adam, Kim, Melissa)

scala> val caps2 = for (f <- people) yield f.capitalize
caps2: List[String] = List(Adam, Kim, Melissa)

    可是一旦當你添加guard(警衛)的時候,for/yield便可購就再也不直接等於map方法調用了。拂過你嘗試使用if字句在你的算法中,而後把這個函數傳遞給map方法,你會發現你獲得了與你想象中不一樣的結果:scala

scala> val fruits = List("apple", "banana", "lime", "orange", "raspberry")
fruits: List[String] = List(apple, banana, lime, orange, raspberry)

scala> fruits.map{ fruit => {
     | if(fruit.length < 6) fruit.toUpperCase
     | }}
res5: List[Any] = List(APPLE, (), LIME, (), ())

    這樣是不行的,可是你可使用filter方法來對集合元素進行過濾,而後再執行map方法調用:

scala> fruits.filter(_.length < 6).map(_.toUpperCase)
res6: List[String] = List(APPLE, LIME)
相關文章
相關標籤/搜索