[scala] 스칼라 컬렉션을지도로 바꾸는 가장 좋은 방법은 무엇입니까?

나는 모음이있는 경우 c유형을 T하고, 등록 정보가 pT(유형 P, 말)하는 할 수있는 가장 좋은 방법은 무엇 맵별로 추출 키는 ?

val c: Collection[T]
val m: Map[P, T]

한 가지 방법은 다음과 같습니다.

m = new HashMap[P, T]
c foreach { t => m add (t.getP, t) }

그러나 이제 변경 가능한 맵이 필요합니다 . 이 작업을 수행하는 더 좋은 방법이 있습니까? 한 줄에 있고 불변의 맵으로 끝납니다 . (Java에서와 마찬가지로 위의 내용을 간단한 라이브러리 유틸리티로 바꿀 수는 있지만 Scala에서는 필요가 없다고 생각합니다)



답변

당신이 사용할 수있는

c map (t => t.getP -> t) toMap

그러나이 과정에는 2 개의 순회가 필요합니다.


답변

가변 개수의 튜플로 맵을 구성 할 수 있습니다. 따라서 컬렉션에서 map 메소드를 사용하여 튜플 컬렉션으로 변환 한 다음 : _ * 트릭을 사용하여 결과를 변수 인수로 변환하십시오.

scala> val list = List("this", "maps", "string", "to", "length") map {s => (s, s.length)}
list: List[(java.lang.String, Int)] = List((this,4), (maps,4), (string,6), (to,2), (length,6))

scala> val list = List("this", "is", "a", "bunch", "of", "strings")
list: List[java.lang.String] = List(this, is, a, bunch, of, strings)

scala> val string2Length = Map(list map {s => (s, s.length)} : _*)
string2Length: scala.collection.immutable.Map[java.lang.String,Int] = Map(strings -> 7, of -> 2, bunch -> 5, a -> 1, is -> 2, this -> 4)


답변

@James Iry의 솔루션 외에도 접기를 사용하여이 작업을 수행 할 수도 있습니다. 이 솔루션이 튜플 방법보다 약간 빠르다고 생각합니다 (쓰레기 객체가 적습니다).

val list = List("this", "maps", "string", "to", "length")
val map = list.foldLeft(Map[String, Int]()) { (m, s) => m(s) = s.length }


답변

이것은 다음과 같이 컬렉션을 통해 접음으로써 불변 적으로 단일 순회로 구현 될 수 있습니다.

val map = c.foldLeft(Map[P, T]()) { (m, t) => m + (t.getP -> t) }

불변 맵에 추가하면 추가 항목이있는 새로운 불변 ​​맵이 반환되고이 값은 접기 작업을 통해 누산기 역할을하기 때문에 솔루션이 작동합니다.

여기서의 단점은 코드의 단순성 대 효율성입니다. 따라서 대규모 콜렉션의 경우이 방법은 map및 적용 과 같은 2 개의 순회 구현을 사용하는 것보다 더 적합 할 수 있습니다 toMap.


답변

다른 솔루션 (일부 유형에서는 작동하지 않을 수 있음)

import scala.collection.breakOut
val m:Map[P, T] = c.map(t => (t.getP, t))(breakOut)

이것은 중개자 목록의 생성을 피합니다. 자세한 정보는 여기 :
Scala 2.8 breakOut


답변

당신이 달성하려는 것은 약간 정의되지 않은 것입니다.
두 개 이상의 항목 c이 동일한 것을 공유하면 p어떻게됩니까? p지도에서 어떤 항목이 해당 항목에 매핑 됩니까?

이것을보다 정확하게 보는 방법 은 그것을 가진 p모든 c항목과 그 사이에 맵을 생성하는 것입니다.

val m: Map[P, Collection[T]]

이것은 groupBy 로 쉽게 달성 할 수 있습니다 .

val m: Map[P, Collection[T]] = c.groupBy(t => t.p)

원래지도를 계속 원한다면 예를 들어 p첫 번째 지도 를 매핑 할 수 있습니다 t.

val m: Map[P, T] = c.groupBy(t => t.p) map { case (p, ts) =>  p -> ts.head }


답변

이것은 목록을 맵으로 전환하는 가장 효율적인 방법은 아니지만 호출 코드를 더 읽기 쉽게 만듭니다. 암시 적 변환을 사용하여 mapBy 메소드를 List 에 추가했습니다 .

implicit def list2ListWithMapBy[T](list: List[T]): ListWithMapBy[T] = {
  new ListWithMapBy(list)
}

class ListWithMapBy[V](list: List[V]){
  def mapBy[K](keyFunc: V => K) = {
    list.map(a => keyFunc(a) -> a).toMap
  }
}

호출 코드 예 :

val list = List("A", "AA", "AAA")
list.mapBy(_.length)                  //Map(1 -> A, 2 -> AA, 3 -> AAA)

암시 적 변환으로 인해 호출자 코드는 스칼라의 implicitConversions를 가져와야합니다.