如何更新嵌套的不可变映射

bih*_*zad 3 scala clojure immutability

我正在尝试找到一种更简洁的方法来更新 Scala 中的嵌套不可变结构。我想我正在寻找类似于assoc-inClojure 的东西。我不确定其中有多少类型因素。

例如,在 Clojure 中,要更新嵌套地图的“city”属性,我会执行以下操作:

> (def person {:name "john", :dob "1990-01-01", :home-address {:city "norfolk", :state "VA"}})
#'user/person
> (assoc-in person [:home-address :city] "richmond")
{:name "john", :dob "1990-01-01", :home-address {:state "VA", :city "richmond"}}
Run Code Online (Sandbox Code Playgroud)

我在 Scala 中有哪些选择?

val person = Map("name" -> "john", "dob" -> "1990-01-01", 
             "home-address" -> Map("city" -> "norfolk", "state" -> "VA"))
Run Code Online (Sandbox Code Playgroud)

Nat*_*ord 5

正如另一个答案所示,您可以利用它case classes来获取更清晰的类型化数据对象。但如果您只需要更新地图:

val m = Map("A" -> 1, "B" -> 2)
val m2 = m + ("A" -> 3)
Run Code Online (Sandbox Code Playgroud)

结果(在工作表中):

m: scala.collection.immutable.Map[String,Int] = Map(A -> 1, B -> 2)
m2: scala.collection.immutable.Map[String,Int] = Map(A -> 3, B -> 2)
Run Code Online (Sandbox Code Playgroud)

+a 上的运算符将Map添加新的键值对,如果已存在则覆盖它。但值得注意的是,因为原始值是 a val,所以您必须将结果分配给新的val,因为您无法更改原始值。

因为在您的示例中,您正在重写嵌套值,所以手动执行此操作会变得更加繁重:

val m = Map("A" -> 1, "B" -> Map("X" -> 2, "Y" -> 4))
val m2 = m + ("B" -> Map("X" -> 3))
Run Code Online (Sandbox Code Playgroud)

这会产生一些数据丢失(嵌套Y值消失):

m: scala.collection.immutable.Map[String,Any] = Map(A -> 1, B -> Map(X -> 2, Y -> 4))
m2: scala.collection.immutable.Map[String,Any] = Map(A -> 1, B -> Map(X -> 3))  // Note that 'Y' has gone away.
Run Code Online (Sandbox Code Playgroud)

因此,迫使您复制原始值,然后将其重新分配回来:

val m = Map("A" -> 1, "B" -> Map("X" -> 2, "Y" -> 4))
val b = m.get("B") match {
  case Some(b: Map[String, Any]) => b + ("X" -> 3)  // Will update `X` while keeping other key-value pairs
  case None => Map("X" -> 3)
}
val m2 = m + ("B" -> b)
Run Code Online (Sandbox Code Playgroud)

这会产生“预期”结果,但显然有很多代码:

m: scala.collection.immutable.Map[String,Any] = Map(A -> 1, B -> Map(X -> 2, Y -> 4))
b: scala.collection.immutable.Map[String,Any] = Map(X -> 3, Y -> 4)
m2: scala.collection.immutable.Map[String,Any] = Map(A -> 1, B -> Map(X -> 3, Y -> 4))
Run Code Online (Sandbox Code Playgroud)

简而言之,对于任何不可变的数据结构,当您“更新”它时,您实际上是在复制所需的所有部分,然后在适当的情况下包含更新的值。如果结构很复杂,这可能会变得很繁重。因此,@0___ 给出的建议是Monocle