2017-10-06 113 views
5

Trochę utknąłem na tym problemie. Czuję, że "myślę w tył" i trochę mnie to dezorientuje.Funkcjonalny sposób dzielenia mapy list na listę map

Mam Map[Long, Seq[String]], który chciałbym przekonwertować na Seq[Map[Long, String]]. Idąc w przeciwnym kierunku jest dość prosty, ponieważ możemy po prostu pogrupować elementy razem, jednak nie jestem pewien, jak podzielić to w sposób funkcjonalny.

Więc

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

powinna stać

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

Myślałam wzdłuż linii, a następnie za pomocą x.partition recursing na każdego powstałego krotki, ale nie jestem pewien, co ja partycja na:/

Piszę w scala, ale każda odpowiedź funkcjonalna jest mile widziana (język nie jest agnostyczny).

+0

Jestem raczej ciekawy, dlaczego potrzebujesz tej operacji. Wydaje się to nieco zaskakujące. – dfeuer

Odpowiedz

5

W 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 i M.fromList konwertować mapy do listy par stowarzyszenia iz powrotem.

map ((,) i) xs jest taki sam jak [(i,x) | x<-xs], dodając (i,...) do każdego elementu.

transpose wymienia "wiersze" i "kolumny" na liście list, podobnie jak transpozycja macierzy.

+0

Bardzo podoba mi się to rozwiązanie. dzięki! – puzzlement

4

W 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 
2

Oto moja odpowiedź w SML (przy użyciu tylko biblioteki standard):

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 

To działa dla mnie:

# 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")]] 
5

Pożyczanie schludny transpose Metoda z tego SO answer, oto inny sposób, aby to zrobić:

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