9

나는 스칼라에서 "직사각형"컬렉션 콜렉션을 종종 옮겨야한다. 예를 들면 :지도 목록, 목록의지도,지도의지도, 일련의 목록 컬렉션 집합은 특정 도메인에서 공동 도메인으로의 매핑으로 일관되게 볼 수 있기 때문에 (예 : List [A]/Array [A]는 Int 도메인에서 A co- 도메인, 집합 [A]는 A 도메인에서 부울 공동 도메인 등으로의 매핑입니다.) 전치 연산을 수행하기 위해 깨끗하고 일반적인 함수를 작성하고 싶습니다 (예 : 목록의지도를 전치 지도 목록). 그러나() 연산자가 아닌 Scala가 컬렉션을 추상적으로 매핑으로 볼 수있는 통일 된 API를 가지고 있지 않기 때문에 문제가 있습니다.스칼라에서 컬렉션 임의 전환하기

def transposeMapOfLists[A,B](mapOfLists: Map[A,List[B]]) : List[Map[A,B]] = { 
    val k = (mapOfLists keys) toList 
    val l = (k map { mapOfLists(_) }) transpose; 
    l map { v => (k zip v) toMap } 
} 

def transposeListOfMaps[A,B](listOfMaps: List[Map[A,B]]) : Map[A,List[B]] = { 
    val k = (listOfMaps(0) keys) toList 
    val l = (listOfMaps map { m => k map { m(_) } }) transpose; 
    (k zip l) toMap 
} 

def transposeMapOfMaps[A,B,C](mapOfMaps: Map[A,Map[B,C]]) : Map[B,Map[A,C]] = { 
    val k = (mapOfMaps keys) toList 
    val listOfMaps = k map { mapOfMaps(_) } 
    val mapOfLists = transposeListOfMaps(listOfMaps) 
    mapOfLists map { p => (p._1, (k zip p._2) toMap) } 
} 

누군가가 나를 트랜스 하나의 일반적인 컬렉션의-컬렉션에 이러한 방법을 통일 도울 수 :

그래서 나는 다음과 같이 컬렉션의-컬렉션의 각 유형에 대해 별도의 트랜스를 작성 결국? 또한이 과정에서 유용한 스칼라 기능을 배울 수 있도록 도와 줄 것입니다.

ps : 예외 처리를 무시하고 입력 컬렉션을 직사각형으로 가정했습니다. 즉, 내부 컬렉션의 모든 도메인 요소가 동일한 집합을 구성합니다.

답변

8

형식 클래스를 사용하는 다음 버전은 확실하게 정리할 수 있지만 빠른 개념 증명으로 작동합니다. 나는 (나는 그것이 가능하다고 확신)를 마우스 오른쪽 의존하는 방법의 유형없이 반환 형식을 얻을 수있는 쉬운 방법을 볼 수 없습니다, 그래서 당신은 -Xexperimental 사용해야합니다 : 몇 가지 테스트를위한

trait Mapping[A, B, C] { 
    type M[D] <: PartialFunction[A, D] 
    def domain(c: C): Seq[A] 
    def fromPairs[D](ps: Seq[(A, D)]): M[D] 
    def codomain(c: C)(implicit ev: C <:< PartialFunction[A, B]) = 
    domain(c).map(c) 
    def toPairs(c: C)(implicit ev: C <:< PartialFunction[A, B]) = 
    domain(c).map(a => (a, c(a))) 
} 

implicit def seqMapping[A, B <: Seq[A]] = new Mapping[Int, A, B] { 
    type M[C] = Seq[C] 
    def domain(c: B) = 0 until c.size 
    def fromPairs[C](ps: Seq[(Int, C)]) = ps.sortBy(_._1).map(_._2) 
} 

implicit def mapMapping[A, B, C <: Map[A, B]] = new Mapping[A, B, C] { 
    type M[D] = Map[A, D] 
    def domain(c: C) = c.keys.toSeq 
    def fromPairs[D](ps: Seq[(A, D)]) = ps.toMap 
} 

def transpose[A, B, C, M, N](m: M)(implicit 
    pev: M <:< PartialFunction[A, N], 
    qev: N <:< PartialFunction[B, C], 
    mev: Mapping[A, N, M], 
    nev: Mapping[B, C, N] 
) = nev.fromPairs(nev.domain(mev.codomain(m).head).map(b => 
    b -> mev.fromPairs(mev.toPairs(m).map { case (a, c) => a -> c(b) }) 
)) 

그리고 지금을 :

scala> println(transpose(List(Map("a" -> 1, "b" -> 13), Map("b" -> 99, "a" -> 14)))) 
Map(a -> Vector(1, 14), b -> Vector(13, 99)) 

scala> println(transpose(Map('a' -> List(1, 2, 3), 'z' -> List(4, 5, 6)))) 
Vector(Map(a -> 1, z -> 4), Map(a -> 2, z -> 5), Map(a -> 3, z -> 6)) 

scala> println(transpose(Map("x" -> Map(4 -> 'a, 99 -> 'z), "y" -> Map(4 -> 'b, 99 -> 's)))) 
Map(4 -> Map(x -> 'a, y -> 'b), 99 -> Map(x -> 'z, y -> 's)) 

원하는대로 작동합니다.

+0

감사합니다. 매우 유용합니다! Scala의 고급 기능 중 일부에 익숙하지 않았기 때문에 수행 한 작업을 이해하는 데 꽤 시간이 걸렸습니다 (지금이 기능을 자세히 배우는 것은 대단한 변명입니다!). – Ashwin