2016-09-01 6 views
2

스컬라의 중첩 불변 인 구조를보다 깨끗하게 갱신하는 방법을 찾고 있습니다. Clojure에서 assoc-in과 비슷한 것을 찾고 있다고 생각합니다. 나는 얼마나 많은 타입이 이것에 영향을 미치는지 확신하지 못한다. 스칼라에서 내 옵션은 무엇중첩 된 불변의 맵을 갱신하는 방법

> (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"}} 

: Clojure의 예를 들어

은 내가 할 것 중첩 된 맵의 "도시"속성을 업데이트하려면? 먼저 임의의 문자열 - 투 - 어떤 문자열에서 멀리 이동하여 코드의 안전성을 향상 할 수 있도록

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

답변

4

스칼라는 정적으로 입력 된 언어입니다.

case class Address(city: String, state: String) 
case class Person(name: String, dob: java.util.Date, homeAddress: Address) 

(예, java.util.Date에 대한 better alternatives있다).

그런 다음이 같은 업데이트를 만들 :

val person = Person(name = "john", dob = new java.util.Date(90, 0, 1), 
    homeAddress = Address(city = "norfolk", state = "VA")) 

person.copy(homeAddress = person.homeAddress.copy(city = "richmond")) 

이 중첩 copy를 방지하려면 Monocle 또는 Quicklens처럼 렌즈 라이브러리를 사용하는 것를 (많은 사람들이 있습니다).

import com.softwaremill.quicklens._ 
person.modify(_.homeAddress.city).setTo("richmond") 
+2

특히,'Map [String, Any]'는 Scala에서 재미 있지 않을 것입니다. – Ryan

+0

@ Ryan : 어떤 수집 유형 서명의 "모든"나쁜 기호가 존재하지 않습니까? List [Any] etc. – Samar

+0

예, 타입 시스템이 'Any'를 추론해야 할 때마다 실수했을 가능성이 있습니다. – Ryan

2

다른 대답 바와 같이, 당신이 청소기 얻을 case classes을 활용할 수는 데이터 개체를 입력했습니다. (워크 시트)

val m = Map("A" -> 1, "B" -> 2) 
val m2 = m + ("A" -> 3) 

결과 :하지만 경우에 당신이 필요가 맵을 업데이트하는 것입니다

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

Map+ 운영자가 새로운 키 - 값 쌍을 추가 할 것입니다, 덮어 쓰기가 이미 존재하는 경우. 원래 값이 val이므로 원래 값을 변경할 수 없기 때문에 결과를 새로운 val에 지정해야합니다.

, 귀하의 예제에서,이 수동으로 다소 부담하게하고, 중첩 된 값을 다시 작성하고 있기 때문에이 일부 손실의 데이터를 산출

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

(중첩 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. 

따라서, 다음 원래 값을 복사 할 강제로 그것을 다시 재 지정 :

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) 
,451,515,

이는 '예상'결과를 얻을 수 있지만, 분명히 많은 코드입니다 : 요컨대

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)) 

, 어떤 불변의 데이터 구조와 때 '갱신'그것은 당신이 정말 당신이 원하는 모든 조각을 복사하는과 적절한 경우 업데이트 된 값을 포함합니다. 구조가 복잡하면 번거로울 수 있습니다. 따라서 @ 0 ___이 (가) Monocle과 같이 제공 한 권장 사항.

+0

중첩 된 예제에서 "데이터 손실"이 어떻게 발생하는지 설명 할 수 있습니까? – Samar

+0

@Samar 답변을 업데이트했습니다. 특히 중첩 된지도에서 'Y'가 사라집니다. –

+0

Y 값을 유지하려면 val m2 = m + ("B"-> Map ("X"-> 3, "Y"-> 4)) 할 수 있습니다. – Samar

0

다른 두 답변은 정확하게 모델링의 중요성을 요약하여 Map [String, Object] 유형의 컬렉션을 처리하지 않아도됩니다.

스칼라의 조용한 강력한 기능 파이프 라이닝 및 고차원 함수 기능을 사용하여 무차별 솔루션으로 제 2 센트를 여기에 추가하십시오. 맵 값이 다른 유형이므로 Scala는 Map 시그니처를 Map [String, Any]로 취급하기 때문에 추한 asInstanceOf 캐스팅이 필요합니다.

val person: Map[String,Any] = Map("name" -> "john", "dob" -> "1990-01-01", "home-address" -> Map("city" -> "norfolk", "state" -> "VA")) 

val newperson = person.map({case(k,v) => if(k == "home-address") v.asInstanceOf[Map[String,String]].updated("city","Virginia") else k -> v}) 
관련 문제