簡體   English   中英

在Scala中轉置任意集合集合

[英]Transposing arbitrary collection-of-collections in Scala

我必須經常在Scala中轉換“矩形”集合集合,例如:地圖列表,列表地圖,地圖地圖,一組列表,集合地圖等。因為集合可以統一被視為從特定域到共域的映射(例如:List [A] / Array [A]是從Int域到A域的映射,Set [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 ) }
}

有人可以幫助我將這些方法統一到一個通用的集合轉置集合中嗎? 它還將幫助我(我相信其他人)在此過程中學習一些有用的Scala功能。

ps:我忽略了異常處理,並假設輸入集合集合是矩形的,即所有內部集合的域元素構成相同的集合。

我敢肯定以下使用類型類的凌亂版本可以清理很多,但它可以作為一個快速的概念驗證。 我沒有看到一種簡單的方法來獲得沒有依賴方法類型的返回類型(我確信它是可能的),所以你必須使用-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))

所以它按照預期工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM