Functional way to split map lists into map lists

I am a bit stuck with this problem. I feel like I am “thinking backwards,” and that is a bit confusing to me.

I have a Map[Long, Seq[String]] that I would like to convert to Seq[Map[Long, String]] . Moving in the other direction is quite simple, since we can simply group elements together, however, I'm not sure how to divide it into functions.

So,

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

should become

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

I was thinking about how to use x.partition , and then recursively on each resulting tuple, but I'm not quite sure what I would split: /

I write in scala, but any functional answer is welcome (language agnostic).

+5
source share
4 answers

In 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.toList and M.fromList convert the map to a list of association pairs and vice versa.

map ((,) i) xs coincides with [(i,x) | x<-xs] [(i,x) | x<-xs] , adding (i,...) to each element.

transpose swaps “rows” and “columns” in a list of lists, similar to transposing a matrix.

+5
source

Borrowing a neat transpose method from this SO answer , here is another way to do this:

 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) // ) 
+5
source

In 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 
+4
source

Here is my answer in OCaml (using only the standard library):

 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 kvm) 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 

This works for me:

 # let m = of_bindings [(1, ["a"; "b"; "c"]); (2, ["d"; "e"]); (3, ["f"])];; val m : string list Mt = <abstr> # let ms = splitmap m;; val ms : string Mt 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")]] 
+2
source

Source: https://habr.com/ru/post/1272402/


All Articles