2015-02-07 17 views
7

我知道有關類似的問題。他們不幫助我 - 如果沒有現有密鑰,代碼不起作用。不錯的方式來添加數字元素在斯卡拉地圖如果密鑰存在或插入新的元素它不是

我只需要一些很好的方法來將Map添加到現有的鍵(如果它存在)或將NEW鍵(如果map不包含適當的鍵)添加到現有的鍵。

下面的代碼工作,但我不喜歡它:

val a = collection.mutable.Map(("k1" -> 1), ("k2" -> 5)) 
val key = "k1" 

val elem = a.get(key) 
if (elem == None) { 
    a += ("k5" -> 200) 
} else { 
    a.update(key, elem.get + 5) 
} 

任何一點更好的? 當前的Scala版本是2.10.4,我目前無法切換到2.11。 可變映射不是100%的限制,但是是首選。

這裏是,例如,similar question,但我也需要考慮不存在的鑰匙的情況。至少我們應該瞭解a.get(key)可能是None或者添加一些更好的方法。好主意是|+|但我想保持基本的Scala 2.10.x.

回答

6

您可以創建你自己的函數,該函數的目的:

def addOrUpdate[K, V](m: collection.mutable.Map[K, V], k: K, kv: (K, V), 
         f: V => V) { 
    m.get(k) match { 
    case Some(e) => m.update(k, f(e)) 
    case None => m += kv 
    } 
} 

addOrUpdate(a, "k1", "k5" -> 200, (v: Int) => v + 5) 
+1

可能現在它是最好的選擇。但是爲什麼Scala複雜的容器庫不能提供開箱即用的簡單東西 - 使用key和value可以增加值(如果不存在任何鍵)或者使用當前值和我的值都可以做些什麼?下次肯定會把這個至少包括到公司的圖書館(魔鬼)中。 – 2015-02-07 21:16:52

+5

這是非常奇特的恕我直言:如果一個**其他**不存在,添加一個鍵不是一個真正的主流行爲 – 2015-02-07 21:21:47

+1

爲什麼?例如,我想通過key來形成聚合地圖,我需要逐個添加元素,所以像'reduce()'這樣的東西不能被使用? – 2015-02-07 21:32:14

7

做到這一點的最簡單的辦法:

a += a.get(key).map(x => key -> (x + 5)).getOrElse("k5" -> 200) 

一般:

a += a.get(k).map(f).map(k -> _).getOrElse(kv) 

相同的,如果你的字典不可變:

m + m.get(k).map(f).map(k -> _).getOrElse(kv) 

所以我沒有看到任何理由在這裏使用可變集合。

如果你不喜歡所有這些Option.map事情:

m + (if (m.contains(k)) k -> f(m(k)) else kv) 

注意,有一整類可能的變化:

k1 -> f(m(k1)) else k2 -> v2 //original 
k1 -> f(m(k1)) else k1 -> v2 
k1 -> f(m(k2)) else k2 -> v2 
k1 -> f(m(k2)) else k1 -> v2 
k2 -> v2 else k1 -> f(m(k1)) 
k1 -> v2 else k1 -> f(m(k1)) 
k2 -> v2 else k1 -> f(m(k2)) 
k1 -> v2 else k1 -> f(m(k2)) 
... //v2 may also be a function from some key's value 

那麼,爲什麼它不是一個標準的功能?國際海事組織,因爲所有的變化仍然可以作爲單線實施。如果你想擁有所有功能的圖書館,可以實施爲單行,你知道,這是斯卡拉:)。

P.S.如果宇還奇怪爲什麼沒有「更新(d)如果堅持」功能 - 見@Rex克爾的回答here

1

一個莫名其妙清晰的方式來做到這一點:

val a = collection.mutable.Map[String, Int]() withDefault insertNewValue 

def insertNewValue(key: String): Int = 
    a += key -> getValueForKey(key) 
    a(key) 
} 

def getValueForKey(key: String): Int = key.length 

不過,我完全disencourage使用可變集合。最好將內部可變狀態保持爲包含不可變字段的變量。

這是因爲一個簡單的規則,除非完全有必要,否則不應暴露自己的內部狀態,如果這樣做,應該儘可能減少可能產生的潛在副作用。

如果您公開一個可變狀態的引用,任何其他actor可以更改它的值,從而失去參考透明度。所有對可變集合的引用都很長,並且很難使用,這並非偶然。這是一個開發人員給你的信息

毫不奇怪,代碼仍然保持相同,在地圖實例化時有一些最小的變化。

var a = Map[String, Int]() withDefault insertNewValue 

def insertNewValue(key: String): Int = { 
    a += key -> getValueForKey(key) 
    a(key) 
} 

def getValueForKey(key: String): Int = key.length 
相關問題