2015-12-25 219 views
0
使用鍵地圖

我與尋找解決斯卡拉以下問題優雅FP辦法掙扎:轉換列表中斯卡拉

說我有一組候選鍵

val validKeys = Set("key1", "key2", "key3") 

和列表該

  1. 開始於一個鍵
  2. 具有非鍵的一些數(> 0)的每個鍵
  3. 之間
  4. 不帶鑰匙

例如結尾:

val myList = List("key3", "foo", "bar", "key1", "baz") 

我想通過使用有效鍵爲重點和聚集非鍵作爲選擇到此列表轉換成地圖值。所以,在上面的例子中:

("key3" -> "foo\nbar", "key1" -> "baz") 

在此先感謝。

+0

如果同一個鍵在列表中多次出現,會發生什麼? –

+0

好問題,假設沒有重複的鍵。 – Dan

回答

2

短期和簡單:

def create(a: List[String]): Map[String, String] = a match { 
    case Nil => Map() 
    case head :: tail => 
     val (vals, rest) = tail.span(!validKeys(_)) 
     create(rest) + (head -> vals.mkString("\n")) 
} 
+0

heh。我只寫了一些與變量名相同的東西:)如果鍵不止出現一次,它不會收集所有的值,但這可能不是OP中可能發生的情況。它不是尾遞歸,但很容易實現。 –

+0

你使用追加到字符串。 Scala中的字符串是不可變的,因此整個字符串在追加時被複制。最好收集所有字符串並一次加入。 –

+0

同意。 'foldLeft'的問題是你沒有機會在最後收集所有的字符串(它可以轉換爲字符串列表,然後做一個mkString的地圖,但這使得它不太清楚)。 –

0

作爲首要的解決方案:

def group(list:List[String]):List[(String, List[String])] = { 
    @tailrec 
    def grp(list:List[String], key:String, acc:List[String]):List[(String, List[String])] = 
    list match { 
     case Nil => List((key, acc.reverse)) 
     case x :: xs if validKeys(x) => (key, acc.reverse)::group(x::xs) 
     case x :: xs => grp(xs, key, x::acc) 
    } 
    list match { 
    case Nil => Nil 
    case x::xs => grp(xs, x, List()) 
    } 
} 

val map = group(myList).toMap 

另一種選擇:

list.foldLeft((Map[String, String](), "")) { 
    case ((map, key), item) if validKeys(item) => (map, item) 
    case ((map, key), item) => 
    (map.updated(key, map.get(key).map(v => v + "\n" + item).getOrElse(item)), key) 
}._1 
1

遍歷列表,從左至右,積累的結果應該建議foldLeft

myList.foldLeft((Map[String, String](), "")) { 
    case ((m, lk), s) => 
     if (validKeys contains s) 
     (m updated (s, ""), s) 
     else (m updated (lk, if (m(lk) == "") s else m(lk) + "\n" + s), lk) 
    }._1 
    // Map(key3 -> foo\nbar, key1 -> baz)