Making Adjacency List in Scala with FoldLeft - scala

Given this function
def makeAdjacencyList(edgeList: Seq[(String, String)]): Map[String, Seq[String]] = {
val emptyMap = Map[String, Seq[String]]().withDefaultValue(Seq[String]())
edgeList.foldLeft(emptyMap) {
case (acc, curr) => {
acc + (curr._1 -> (acc(curr._1) :+ curr._2))
acc + (curr._2 -> (acc(curr._2) :+ curr._1))
}
}
}
It takes in a edgeList of an undirected graph, for example:
Vector((S1,E1), (S1,D1), (S2,E2), (S2,D1), (S3,E1), (S3,D2))
The function however gives an output of
[E1 -> [S1, S3], D1 -> [S1, S2], E2 -> [S2], D2 -> [S3]]
but I was expecting an output of
[S1 -> [E1, D1], S2 -> [E2, D1], S3 -> [E1], E1 -> [S1, S3], D1 -> [S1, S2], E2 -> [S2], D2 -> [S3]]
It seems like the first line of the foldLeft (acc + (curr._1 -> (acc(curr._1) :+ curr._2))) is being ignored and I am not quite sure why.

Both lines of code in the case block produces a result, but only the 2nd result is returned and becomes the next acc value.
This would fix it.
edgeList.foldLeft(emptyMap) {
case (acc, curr) =>
acc + (curr._1 -> (acc(curr._1) :+ curr._2)) +
(curr._2 -> (acc(curr._2) :+ curr._1))
}
Here, on the other hand, is a different approach to achieve the desired output.
def makeAdjacencyList(edgeList: Seq[(String,String)]): Map[String,Seq[String]] =
edgeList.flatMap(edge => Seq(edge, edge.swap))
.groupMap(_._1)(_._2)

Related

Functional way to split a map of lists into a list of maps

I'm a bit stuck on this problem. I feel like I'm "thinking backwards" and it's confusing me a bit.
I have a Map[Long, Seq[String]] which I would like to convert into a Seq[Map[Long, String]]. Going the other direction is rather simple, as we can just group elements together, however, I'm not sure how to split this apart in a functional manner.
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 along the lines of using x.partition and then recursing on each resulting tuple, but I'm not really sure what I'd partition on :/
I'm writing in scala, but any functional answer is welcome (language agnostic).
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 a map to a list of association pairs, and back.
map ((,) i) xs is the same as [(i,x) | x<-xs], adding (i,...) to each element.
transpose exchanges the "rows" and "columns" in a list of lists, similarly to a matrix transposition.
Borrowing a neat transpose method from this SO answer, here's another way to do it:
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)
// )
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
Here is my answer in OCaml (using just 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 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
It works for me:
# 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")]]

Recursively walk values in Map

I am attempting to walk a Map[String,List[String]] recursively to extract and flatten all values associated with a Map
val x = Map("a" -> List("b","c","d"), "b" -> List("f","g","h"), "f" -> List("i","j","k"), "g" -> List("p","q","r"))
For each of the keys, extract values i.e. List
For each item in values of List:
Check if key exists and then extract values
Continue to do so recursively till the keys have no values and flatten values of list for key
The result should be
Map("a" -> List("b","c","d","f","g","h","i","j","k","p","q","r"),
"b" -> List("f","g","h","i","j","k","p","q","r"),
"f" -> List("i","j","k"),
"g" -> List("p","q","r"))
You can try to iterate until there is no change:
def getValues(dict: Map[String, List[String]]) = Iterator.iterate(dict) { _.mapValues {
_.flatMap(v => v :: dict.get(v).toList.flatten).toSet.toList
} filterNot { _._2.isEmpty }
}.sliding(2) find { x => x.head == x.last }
This definitely is not the most efficient solution, but it is pretty terse!
Try this code:
def f(map: Map[String, List[String]]): Map[String, List[String]] = {
def f(x: Map[String, List[String]], acc: Map[String, List[String]]): Map[String, List[String]] = {
if (x.isEmpty) acc
else {
val keys = x.keySet
val (complex, simple) = x partition {_._2 exists {s => keys contains s}}
val newX =
(for ((ck, cl) <- complex)
yield (ck -> (simple.filter(x => cl.contains (x._1)).map(_._2).flatten ++ cl).toList)).toMap
f(newX, acc ++ simple)
}
}
f(map, Map.empty)
}
val x = Map("a" -> List("b","c","d"), "b" -> List("f", "g", "h"), "f" -> List("i","j","k"), "g" -> List("p","q","r"))
println(f(x)) //Map(f -> List(i, j, k), g -> List(p, q, r), b -> List(i, j, k, p, q, r, f, g, h), a -> List(i, j, k, p, q, r, f, g, h, b, c, d))
However it is assumed that there is no recursion in the map e.g. ("a" -> List("b")), ("b" -> List("a"). If it happens the function will end up in infinite loop. You would have to add extra code to handle such situations.

What is a more functional way of creating a Map of List?

I have this working code to create a Map between the characters in a String, and a List containing the indexes.
scala> "Lollipop".zipWithIndex.foldLeft(Map[Char, List[Int]]())((acc, t) => acc + (t._1 -> (acc.getOrElse(t._1, List[Int]()) :+ t._2)))
res122: scala.collection.immutable.Map[Char,List[Int]] = Map(i -> List(4), L -> List(0), l -> List(2, 3), p -> List(5, 7), o -> List(1, 6))
But the use of acc.getOrElse looks imperative.
Is there a more functional way that hides this from the user?
for {
(c, l) <- "Lollipop".zipWithIndex.groupBy{ _._1 }
} yield c -> l.map{ _._2 }
// Map(i -> Vector(4), L -> Vector(0), l -> Vector(2, 3), p -> Vector(5, 7), o -> Vector(1, 6))
After groupBy{ _._1 } you'll get a Map[Char, Seq[(Char, Int)]]. So you have to convert pairs (Char, Int) to Int, using p => p._2 or just _._2.
You could use mapValueslike this:
"Lollipop".zipWithIndex.groupBy{ _._1 }.mapValues{ _.map{_._2} }
But mapValues creates a lazy collection, so you could get a performance issue in case of multiple access to the same element by key.
Alternative is to use default value for your map (rewritten code a little bit to be more explicit):
val empty = Map.empty[Char, List[Int]].withDefaultValue(List.empty)
"Lollipop".zipWithIndex.foldLeft(empty) {
case (acc, (char, position)) => {
val positions = acc(char) :+ position
acc + (char -> positions)
}
}

Map a single entry of a Map

I want to achieve something like the following:
(_ : Map[K,Int]).mapKey(k, _ + 1)
And the mapKey function applies its second argument (Int => Int) only to the value stored under k. Is there something inside the standard lib? If not I bet there's something in Scalaz.
Of course I can write this function myself (m.updated(k,f(m(k))) and its simple to do so. But I've come over this problem several times, so maybe its already done?
For Scalaz I imagine something along the following code:
(m: Map[A,B]).project(k: A).map(f: B => B): Map[A,B]
You could of course add
def changeForKey[A,B](a: A, fun: B => B): Tuple2[A, B] => Tuple2[A, B] = { kv =>
kv match {
case (`a`, b) => (a, fun(b))
case x => x
}
}
val theMap = Map('a -> 1, 'b -> 2)
theMap map changeForKey('a, (_: Int) + 1)
res0: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 2, 'b -> 2)
But this would circumvent any optimisation regarding memory re-use and access.
I came also up with a rather verbose and inefficient scalaz solution using a zipper for your proposed project method:
theMap.toStream.toZipper.flatMap(_.findZ(_._1 == 'a).flatMap(elem => elem.delete.map(_.insert((elem.focus._1, fun(elem.focus._2)))))).map(_.toStream.toMap)
or
(for {
z <- theMap.toStream.toZipper
elem <- z.findZ(_._1 == 'a)
z2 <- elem.delete
} yield z2.insert((elem.focus._1, fun(elem.focus._2)))).map(_.toStream.toMap)
Probably of little use. I’m just posting for reference.
Here is one way:
scala> val m = Map(2 -> 3, 5 -> 11)
m: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 11)
scala> m ++ (2, m.get(2).map(1 +)).sequence
res53: scala.collection.immutable.Map[Int,Int] = Map(2 -> 4, 5 -> 11)
scala> m ++ (9, m.get(9).map(1 +)).sequence
res54: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 11)
This works because (A, Option[B]).sequence gives Option[(A, B)]. (sequence in general turns types inside out. i.e. F[G[A]] => [G[F[A]], given F : Traverse and G : Applicative.)
You can pimp it with this so that it creates a new map based on the old one:
class MapUtils[A, B](map: Map[A, B]) {
def mapValueAt(a: A)(f: (B) => B) = map.get(a) match {
case Some(b) => map + (a -> f(b))
case None => map
}
}
implicit def toMapUtils[A, B](map: Map[A, B]) = new MapUtils(map)
val m = Map(1 -> 1)
m.mapValueAt(1)(_ + 1)
// Map(1 -> 2)
m.mapValueAt(2)(_ + 1)
// Map(1 -> 1)

Reverse / transpose a one-to-many map in Scala

What is the best way to turn a Map[A, Set[B]] into a Map[B, Set[A]]?
For example, how do I turn a
Map(1 -> Set("a", "b"),
2 -> Set("b", "c"),
3 -> Set("c", "d"))
into a
Map("a" -> Set(1),
"b" -> Set(1, 2),
"c" -> Set(2, 3),
"d" -> Set(3))
(I'm using immutable collections only here. And my real problem has nothing to do with strings or integers. :)
with help from aioobe and Moritz:
def reverse[A, B](m: Map[A, Set[B]]) =
m.values.toSet.flatten.map(v => (v, m.keys.filter(m(_)(v)))).toMap
It's a bit more readable if you explicitly call contains:
def reverse[A, B](m: Map[A, Set[B]]) =
m.values.toSet.flatten.map(v => (v, m.keys.filter(m(_).contains(v)))).toMap
Best I've come up with so far is
val intToStrs = Map(1 -> Set("a", "b"),
2 -> Set("b", "c"),
3 -> Set("c", "d"))
def mappingFor(key: String) =
intToStrs.keys.filter(intToStrs(_) contains key).toSet
val newKeys = intToStrs.values.flatten
val inverseMap = newKeys.map(newKey => (newKey -> mappingFor(newKey))).toMap
Or another one using folds:
def reverse2[A,B](m:Map[A,Set[B]])=
m.foldLeft(Map[B,Set[A]]()){case (r,(k,s)) =>
s.foldLeft(r){case (r,e)=>
r + (e -> (r.getOrElse(e, Set()) + k))
}
}
Here's a one statement solution
orginalMap
.map{case (k, v)=>value.map{v2=>(v2,k)}}
.flatten
.groupBy{_._1}
.transform {(k, v)=>v.unzip._2.toSet}
This bit rather neatly (*) produces the tuples needed to construct the reverse map
Map(1 -> Set("a", "b"),
2 -> Set("b", "c"),
3 -> Set("c", "d"))
.map{case (k, v)=>v.map{v2=>(v2,k)}}.flatten
produces
List((a,1), (b,1), (b,2), (c,2), (c,3), (d,3))
Converting it directly to a map overwrites the values corresponding to duplicate keys though
Adding .groupBy{_._1} gets this
Map(c -> List((c,2), (c,3)),
a -> List((a,1)),
d -> List((d,3)),
b -> List((b,1), (b,2)))
which is closer. To turn those lists into Sets of the second half of the pairs.
.transform {(k, v)=>v.unzip._2.toSet}
gives
Map(c -> Set(2, 3), a -> Set(1), d -> Set(3), b -> Set(1, 2))
QED :)
(*) YMMV
A simple, but maybe not super-elegant solution:
def reverse[A,B](m:Map[A,Set[B]])={
var r = Map[B,Set[A]]()
m.keySet foreach { k=>
m(k) foreach { e =>
r = r + (e -> (r.getOrElse(e, Set()) + k))
}
}
r
}
The easiest way I can think of is:
// unfold values to tuples (v,k)
// for all values v in the Set referenced by key k
def vk = for {
(k,vs) <- m.iterator
v <- vs.iterator
} yield (v -> k)
// fold iterator back into a map
(Map[String,Set[Int]]() /: vk) {
// alternative syntax: vk.foldLeft(Map[String,Set[Int]]()) {
case (m,(k,v)) if m contains k =>
// Map already contains a Set, so just add the value
m updated (k, m(k) + v)
case (m,(k,v)) =>
// key not in the map - wrap value in a Set and return updated map
m updated (k, Set(v))
}