将列表映射拆分为映射列表的功能方法

时间:2017-10-06 17:13:53

标签: scala haskell functional-programming ocaml

我有点卡在这个问题上。我觉得我在倒退#34;而且让我有点困惑。

我有一个Map[Long, Seq[String]],我想将其转换为Seq[Map[Long, String]]。走向另一个方向相当简单,因为我们可以将元素组合在一起,但是,我不确定如何以功能性的方式将它们分开。

所以,

val x = Map(1 -> List("a","b","c"), 2 -> List("d", "e"), 3 -> List("f"))

应该成为

List(Map(1 -> "a", 2 -> "d", 3 -> "f"), Map(1 -> "b", 2 -> "e"), Map(1 -> "c"))

我正在考虑使用x.partition,然后对每个产生的元组进行递归,但我不确定我要分区的是什么:/

我在scala中写作,但欢迎任何功能性答案(语言不可知)。

4 个答案:

答案 0 :(得分:5)

在Haskell:

> import qualified Data.Map as M
> import Data.List
> m = M.fromList [(1,["a","b","c"]), (2,["d","e"]), (3,["f"])]
> map M.fromList . transpose . map (\(i,xs) -> map ((,) i) xs) . M.toList $ m
[fromList [(1,"a"),(2,"d"),(3,"f")],fromList [(1,"b"),(2,"e")],fromList [(1,"c")]]

M.toListM.fromList将地图转换为关联对列表,然后返回。

map ((,) i) xs[(i,x) | x<-xs]相同,为每个元素添加(i,...)

transpose在列表列表中交换“行”和“列”,类似于矩阵转置。

答案 1 :(得分:5)

SO answer借用一个整洁的transpose方法,这是另一种方法:

def transpose[A](xs: List[List[A]]): List[List[A]] = xs.filter(_.nonEmpty) match {    
  case Nil =>  Nil
  case ys: List[List[A]] => ys.map{ _.head }::transpose(ys.map{ _.tail })
}

transpose[(Int, String)](
  x.toList.map{ case (k, v) => v.map( (k, _) ) }
).map{ _.toMap }

// Res1: List[scala.collection.immutable.Map[Int,String]] = List(
//   Map(1 -> a, 2 -> d, 3 -> f), Map(1 -> b, 2 -> e), Map(1 -> c)
// )

答案 2 :(得分:4)

在Scala中:

val result = x.toList
  .flatMap { case (k, vs) => vs.zipWithIndex.map { case (v, i) => (i, k, v) } } // flatten and add indices to inner lists
  .groupBy(_._1)                 // group by index
  .toList.sortBy(_._1).map(_._2) // can be replaced with .values if order isn't important
  .map(_.map { case (_, k, v) => (k, v) }.toMap) // remove indices

答案 3 :(得分:2)

以下是我在OCaml中的答案(仅使用标准库):

module M = Map.Make(struct type t = int let compare = compare end)

let of_bindings b =
    List.fold_right (fun (k, v) m -> M.add k v m) b M.empty

let splitmap m =
    let split1 (k, v) (b1, b2) =
        match v with
        | [] -> (b1, b2)
        | [x] -> ((k, x) :: b1, b2)
        | h :: t -> ((k, h) :: b1, (k, t) :: b2)
    in
    let rec loop sofar m =
        if M.cardinal m = 0 then
            List.rev sofar
        else
            let (b1, b2) =
                List.fold_right split1 (M.bindings m) ([], [])
            in
            let (ms, m') = (of_bindings b1, of_bindings b2) in
            loop (ms :: sofar) m'
    in
    loop [] m

它对我有用:

# let m = of_bindings [(1, ["a"; "b"; "c"]); (2, ["d"; "e"]); (3, ["f"])];;
val m : string list M.t = <abstr>
# let ms = splitmap m;;
val ms : string M.t list = [<abstr>; <abstr>; <abstr>]
# List.map M.bindings ms;;
- : (M.key * string) list list =
[[(1, "a"); (2, "d"); (3, "f")]; [(1, "b"); (2, "e")]; [(1, "c")]]