2011-05-01 1 views
2

이 질문에 대한 영감은 내가 this one에 대답하려고했을 때 나온 것입니다.중첩 된지도의 가장 안쪽에있는지도의 mapValues ​​

데이터 시퀀스가 ​​있다고 가정 해 봅시다 (예 : CSV 파일에서 가져온 것일 수 있음). groupBy은 데이터의 특정 측면을 분석하고 열 또는 열의 조합으로 그룹화하는 데 사용할 수 있습니다.

val groups2 = 
    groups1.mapValues(groups => groups.mapValues(row => row.groupBy(_(2))) 
: 예를 들어 : 다음 그룹 내에서 하위 그룹을 만들려면 내가 정말 성가신 도착이 더 한 번하고 싶은 경우

val groups0: Map[String, Array[String]] = 
    seq.groupBy(row => row(0) + "-" + row(4)) 

나는

val groups1: Map[String, Map[String, Array[String]]] = 
    groups0.mapValues(row => row.groupBy(_(1)) 

을 수행 할 수 있습니다

Map[K0, Map[K1, ..., Map[Kn, V]]]의 임의의 중첩이 주어진 내 질문은 여기 f: (V) => B을 사용하고 가장 안쪽에 적용되는 mapValues 함수를 작성 하시겠습니까? VMap[K0, Map[K1, ..., Map[Kn, B]]]을 반환 하시겠습니까?

답변

5

나의 첫번째 본능 형태 보증 된 방법으로 임의의 중첩을 처리하는 것은 불가능할 것이라고 말했다, 그러나 당신이 그것을 할 방법을 컴파일러에게 몇 implicits을 정의 할 경우에 가능하다는 것을 보인다.
본질적으로, "간단한"매퍼 "wrappedMapper는"하나 개의지도 계층을 드릴 다운하는 방법을 알려줍니다 반면, 일반 비 중첩 된 사건을 처리하는 방법을 알려줍니다 : 실제 코드에서, 물론

// trait to tell us how to map inside of a container. 
    trait CanMapInner[WrappedV, WrappedB,V,B] { 
    def mapInner(in: WrappedV, f: V => B): WrappedB 
    } 

    // simple base case (no nesting involved). 
    implicit def getSimpleMapper[V,B] = new CanMapInner[V,B,V,B] { 
    def mapInner(in: V, f: (V) => B): B = f(in) 
    } 

    // drill down one level of "Map". 
    implicit def wrappedMapper[K,V,B,InnerV,InnerB] 
    (implicit innerMapper: CanMapInner[InnerV,InnerB,V,B]) = 
    new CanMapInner[Map[K,InnerV], Map[K,InnerB],V,B] { 
     def mapInner(in: Map[K, InnerV], f: (V) => B): Map[K, InnerB] = 
     in.mapValues(innerMapper.mapInner(_, f)) 
    } 

    // the actual implementation. 
    def deepMapValues[K,V,B,WrappedV,WrappedB](map: Map[K,WrappedV], f: V => B) 
     (implicit mapper: CanMapInner[WrappedV,WrappedB,V,B]) = { 
    map.mapValues(inner => mapper.mapInner(inner, f)) 
    } 

    // testing with a simple map 
    { 
    val initMap = Map(1 -> "Hello", 2 -> "Goodbye") 
    val newMap = deepMapValues(initMap, (s: String) => s.length) 
    println(newMap) // Map(1 -> 5, 2 -> 7) 
    } 

    // testing with a nested map 
    { 
    val initMap = Map(1 -> Map("Hi" -> "Hello"), 2 -> Map("Bye" -> "Goodbye")) 
    val newMap = deepMapValues(initMap, (s: String) => s.length) 
    println(newMap) // Map(1 -> Map(Hi -> 5), 2 -> Map(Bye -> 7)) 
    } 

패턴 매칭 다이나믹 솔루션은 단순함 덕분에 유혹을 불러 일으 킵니다. 타입 안전성이 전부는 아닙니다 :)

+0

마지막 문장은 매우 사실입니다. 나는 당신의 해결책과 유진으로 놀기 시작했다. 유진과 함께 빠른 결과를 얻었습니다. 이 과정에서 나는 런타임 캐스팅 예외 (내 오류가 있지만 컴파일러가 그것을 잡았 으면 좋겠다)를 가로 질러 실행했다. 그래서 형 안전함을 시험해 볼 것이라고 생각했습니다. 불행히도, 나는 암묵적으로 발견 된 오류를 겪고 있습니다. 내가 알아낼 수 없다면 나는 내 컴퓨터로 돌아와서 요점을 게시 할 것이다. – huynhjl

+0

좋아, 내가 암시 적으로 범위에 가져 오려고했지만 실패했다. 이것은 귀하의 코드 https://gist.github.com/950580을 사용한 방법입니다. 나는 방법을 사용하는 범위에서 함축을 정의해야했습니다. 처음에는 CanMapInner 동반자 개체에 넣고 가져 오려고했으나 작동하지 않았습니다. – huynhjl

3

, 내가 Manifest를 사용하여 더 나은 방법이 확신하지만, 패턴 매칭이 SeqMap을 구별하는 것 같다 그래서 여기있다 :

object Foo { 
    def mapValues[A <: Map[_, _], C, D](map: A)(f: C => D): Map[_, _] = map.mapValues { 
    case seq: Seq[C] => seq.groupBy(f) 
    case innerMap: Map[_, _] => mapValues(innerMap)(f) 
    } 
} 

scala> val group0 = List("fooo", "bar", "foo") groupBy (_(0)) 
group0: scala.collection.immutable.Map[Char,List[java.lang.String]] = Map((f,List(fooo, foo)), (b,List(bar))) 

scala> val group1 = Foo.mapValues(group0)((x: String) => x(1)) 
group1: scala.collection.immutable.Map[_, Any] = Map((f,Map(o -> List(fooo, foo))), (b,Map(a -> List(bar)))) 

scala> val group2 = Foo.mapValues(group1)((x: String) => x(2)) 
group2: scala.collection.immutable.Map[_, Any] = Map((f,Map(o -> Map(o -> List(fooo, foo)))), (b,Map(a -> Map(r -> List(bar))))) 

편집 : 여기가 높 사용하여 입력 된 버전입니다 kinded 유형.

trait NestedMapValue[Z] { 
    type Next[X] <: NestedMapValue[Z] 
    def nextValues[D](f: Z => D): Next[D] 
} 

trait NestedMap[Z, A, B <: NestedMapValue[Z]] extends NestedMapValue[Z] { self => 
    type Next[D] = NestedMap[Z, A, B#Next[D]] 

    val map: Map[A, B] 
    def nextValues[D](f: Z => D): Next[D] = self.mapValues(f) 

    def mapValues[D](f: Z => D): NestedMap[Z, A, B#Next[D]] = new NestedMap[Z, A, B#Next[D]] { val map = self.map.mapValues { 
    case x: B => x.nextValues[D](f) 
    }} 

    override def toString = "NestedMap(%s)" format (map.toString) 
} 

trait Bottom[A] extends NestedMapValue[A] { 
    type Next[D] = NestedMap[A, D, Bottom[A]] 

    val seq: Seq[A] 
    def nextValues[D](f: A => D): Next[D] = seq match { 
    case seq: Seq[A] => groupBy[D](f) 
    } 

    def groupBy[D](f: A => D): Next[D] = seq match { 
    case seq: Seq[A] => 
     new NestedMap[A, D, Bottom[A]] { val map = seq.groupBy(f).map { case (key, value) => (key, new Bottom[A] { val seq = value })} } 
    } 

    override def toString = "Bottom(%s)" format (seq.toString) 
} 

object Bottom { 
    def apply[A](aSeq: Seq[A]) = new Bottom[A] { val seq = aSeq } 
} 

scala> val group0 = Bottom(List("fooo", "bar", "foo")).groupBy(x => x(0)) 
group0: NestedMap[java.lang.String,Char,Bottom[java.lang.String]] = NestedMap(Map(f -> Bottom(List(fooo, foo)), b -> Bottom(List(bar)))) 

scala> val group1 = group0.mapValues(x => x(1)) 
group1: NestedMap[java.lang.String,Char,Bottom[java.lang.String]#Next[Char]] = NestedMap(Map(f -> NestedMap(Map(o -> Bottom(List(fooo, foo)))), b -> NestedMap(Map(a -> Bottom(List(bar)))))) 

scala> val group2 = group1.mapValues(x => x.size) 
group2: NestedMap[java.lang.String,Char,Bottom[java.lang.String]#Next[Char]#Next[Int]] = NestedMap(Map(f -> NestedMap(Map(o -> NestedMap(Map(4 -> Bottom(List(fooo)), 3 -> Bottom(List(foo)))))), b -> NestedMap(Map(a -> NestedMap(Map(3 -> Bottom(List(bar)))))))) 
관련 문제