I tried to use Map.map to convert a map into a List of Tuples.
However this fails. I did the following experiments:
val m = Map(("a" -> 1), ("b" -> 2))
//> m : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
val r1 = m.map{ case (k,v) => v} //> r1 : scala.collection.immutable.Iterable[Int] = List(1, 2)
def toTuple[A,B](a:A,b:B) = (a,b) //> toTuple: [A, B](a: A, b: B)(A, B)
//val r2: List[Tuple2[_,_]] = m.map(e => (e._1,e._2))
val r3 = m.map(e => toTuple(e._1,e._2)) //> r3 : scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
val r4 = m.toSeq //> r4 : Seq[(String, Int)] = ArrayBuffer((a,1), (b,2))
Notice how a List is generated for single elements (r1) but a Map is produced for tuples (r3). Not even forcing the type worked (r2). Only an explicit call to Seq did it (r4) So my question is, why/how does Map.map "automagically" create a new Map and not a list for example? In fact how is the return type determined (Seq, List, etc.)
A Map is a collection of tuples already.
scala> "b" -> 2
res0: (String, Int) = (b,2) // Implicitly converted to a Tuple
When you're mapping a Map, you're mapping the (key, value) pairs that it contains. This can't work, because you're stripping away the keys, and retaining only the values. So what you have is no longer a Map, but a step or two up the collection hierarchy, an Iterable:
val r1 = m.map{ case (k,v) => v}
Forcing the type cannot work, because a Map[A, B] is not a List[(A, B)]. This is the equivalent of m.map(identity). Notice how you're even accessing e with tuple accessors:
val r2: List[Tuple2[_,_]] = m.map(e => (e._1,e._2))
val r3 = m.map(e => toTuple(e._1,e._2))
Here, Seq is more generalized than List:
val r4 = m.toSeq
The simple solution as stated by #EndeNeu is to just use toList. When you map a collection, it should return the original collection type if it can. So mapping a Map should return another Map, unless the underlying structure has made it no longer a Map (like removing keys entirely) in r1.
Why even bother with a map? Wouldn't it be more appropriate for your use case to just have your data structure be a List[(String,Int)]? It's basically the same.. you can even write it in map notation:
val myMapAsList: List[(String, Int)] = List[(String,Int)](
"a" -> 1,
"b" -> 2
) // yields val myMapAsList: List[(String, Int)] = List((a,1), (b,2))
Related
val l = List(1,2,3)
val f = l flatMap (_ + 1)
I am running the above piece of code in Scala and I am getting the below error.
<console>:12: error: type mismatch;
found : Int(1)
required: String
val f = l flatMap (_ + 1)
What flatMap does is that it executes the map function that we provide (in this case ->: _ + 1) and then it iterates the resultant of map function. If iterator is not available on the map output, it throws this error.
Is my understanding correct?
Yes, but the function you pass to flatMap should produce a List itself. What you're looking for is simply map:
val l = List(1,2,3)
val f = l map (_ + 1)
flatMap is (as the name suggests) consecutive map and flatten. The latter in case of Listturns a nested collection into a flat one:
val nestedList: List[List[A]] = ???
val flattenedList: List[A] = nestedList.flatten
Your example would work if you passed a function producing List to flatMap, examples:
val res1 = l.flatMap(el => List(el + 1)) // add 1 to every element - same as with map above
val res2 = l.flatMap(el => List(el, 1)) // add 1 after every element
I have 2 lists. They will always be the same length with respect to each other and might look like this toy example. The actual content is not predictable.
val original = [1, 2, 0, 1, 1, 2]
val elements = ["a","b","c","d","e","f"]
I want to create the following list:
val mappedList = [["c"],["a","d","e"],["b","f"]]
0 1 2
So the pattern is to group elements in the elements list, based on the value of the same-position element in original list. Any idea how can I achieve this in SML? I am not looking for a hard coded solution for this exact data, but a general one.
One way is to first write a function which takes an ordered pair such as (2,"c") and a list of ordered pairs such as
[(3,["a"]),(2,["b"]),(1,["a","e"])]
and returns a modified list with the element tacked onto the appropriate list (or creates a new (key,list) pair if none exists) so that the result would look like:
[(3,["a"]),(2,["c","b"]),(1,["a","e"])]
The following function does the trick:
fun store ((k,v), []) = [(k,[v])]
| store ((k,v), (m,vs)::items) = if k = m
then (m,v::vs)::items
else (m,vs)::store ((k,v) ,items);
Given a list of keys and a corresponding list of values, you could fold this last function over the corresponding zip of the keys and values:
fun group ks vs = foldl store [] (ListPair.zip(ks,vs));
For example, if
val original = [1, 2, 0, 1, 1, 2];
val elements = ["a","b","c","d","e","f"];
- group original elements;
val it = [(1,["e","d","a"]),(2,["f","b"]),(0,["c"])] : (int * string list) list
Note that you could sort this list according to the keys if so desired.
Finally -- if you just want the groups (reversed to match their original order in the list) the following works:
fun groups ks vs = map rev (#2 (ListPair.unzip (group ks vs)));
For example,
- groups original elements;
val it = [["a","d","e"],["b","f"],["c"]] : string list list
On Edit: if you want the final answer to be sorted according to the keys (as opposed to the order in which they appear) you could use #SimonShine 's idea and store the data in sorted order, or you could sort the output of the group function. Somewhat oddly, the SML Standard Basis Library lacks a built-in sort, but the standard implementations have their own sorts (and it is easy enough to write your own). For example, using SML/NJ's sort you could write:
fun sortedGroups ks vs =
let
val g = group ks vs
val s = ListMergeSort.sort (fn ((i,_),(j,_)) => i>j) g
in
map rev (#2 (ListPair.unzip s))
end;
Leading to the expected:
- sortedGroups original elements;
val it = [["c"],["a","d","e"],["b","f"]] : string list list
With the general strategy to first form a list of pairs (k, vs) where k is the value they are grouped by and vs is the elements, one could then extract the elements alone. Since John did this, I'll add two other things you can do:
Assume that original : int list, insert the pairs in sorted order:
fun group ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case Int.compare (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
This produces the same result as your example:
- val mappedList = group original elements;
> val mappedList = [["c"], ["a", "d", "e"], ["b", "f"]] : string list list
I'm a bit unsure if by "The actual content is not predictable." you also mean "The types of original and elements are not known." So:
Assume that original : 'a list and that some cmp : 'a * 'a -> order exists:
fun group cmp ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case cmp (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
Use a tree for storing pairs:
datatype 'a bintree = Empty | Node of 'a bintree * 'a * 'a bintree
(* post-order tree folding *)
fun fold f e Empty = e
| fold f e0 (Node (left, x, right)) =
let val e1 = fold f e0 right
val e2 = f (x, e1)
val e3 = fold f e2 left
in e3 end
fun group cmp ks vs =
let fun insert ((k, v), Empty) = Node (Empty, (k, [v]), Empty)
| insert (k1v as (k1, v), Node (left, k2vs as (k2, vs), right)) =
case cmp (k1, k2) of
LESS => Node (insert (k1v, left), k2vs, right)
| EQUAL => Node (left, (k2, v::vs), right)
| GREATER => Node (left, k2vs, insert (k1v, right))
fun extract ((k, vs), result) = rev vs :: result
in
fold extract [] (List.foldl insert Empty (ListPair.zip (ks, vs)))
end
Have two Lists :
val list1 : List[List[(String, String)]] = List(List("1" -> "a" , "1" -> "b"))
//> list1 : List[List[(String, String)]] = List(List((1,a), (1,b)))
val list2 : List[List[(String, String)]] = List(List("2" -> "a" , "2" -> "b"))
//> list2 : List[List[(String, String)]] = List(List((2,a), (2,b)))
//Expecting
val toConvert = List(List(Map("1" -> "a" , "2" -> "b"), Map("1" -> "b" , "2" -> "a")))
Attempting to convert these lists to type :
List[List[scala.collection.immutable.Map[String,String]]] = Lis
//| t(List(Map(1 -> a, 2 -> b), Map(1 -> b, 2 -> a)))
Using this code :
val ll = list1.zip(list2).map(m => List(m._1.toMap , m._2.toMap))
But Map entries are missing :
List[List[scala.collection.immutable.Map[String,String]]] = List(List(
//| Map(1 -> b), Map(2 -> b)))
How to convert list1,list2 to type List[List[scala.collection.immutable.Map[String,String]]] which includes values : (List(Map(1 -> a, 2 -> b), Map(1 -> b, 2 -> a))) ?
Update :
Logic of : Map("1" -> "a" , "2" -> "b")
Combine each List using the zip functions :
val p1 : List[(List[(String, String)], List[(String, String)])] = list1.zip(list2);
Convert each element of thenewly created List to a Map and then add to a newly created List :
val p2 = p1.map(m => List(m._1.toMap , m._2.toMap))
How to convert list1 and list2 to type List[List[Map[...
Starting from the type List[List[(String, String)]] we would like to get to the type List[List[Map[String, String]]].
Inner Type
The inner type we want is Map[String, String]. As I asked in the comments, I don't fully understand the expected logic to construct this Map so I am assuming you want to create a Map[String, String] from a list of tuples List[(String, String)].
When creating the Map using .toMap, the key-value elements with the same key will get overwritten as we can see clearly from the b += x in its implementation:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
val b = immutable.Map.newBuilder[T, U]
for (x <- self)
b += x
b.result()
}
(source TraversableOnce.scala)
So the logic we use to create List[(String, String)] will determine the generated Map[String, String].
Outer List of List
Combine each List using the zip functions
type A = List[(String, String)]
val list12: List[(A, A)] = list1 zip list2
Zipping list1 and list2 gives us a list of tuples but the type we want is actually a list of list of tuple: List[List[(A, A)]]
In your example you are getting that type mapping to a list m => List(...). That is the key part. Let me split that in 2 parts to make it clearer:
list12.map(m => List(m._1 , m._2)).map(_.map(_.toMap))
Let's extract that into a separate function:
def keyPart: ((A, A)) => List[A] = { case (l1, l2) => List(l1, l2) }
val resultNotExpected = list12.map(keyPart).map(_.map(_.toMap))
The result is of course the same of the one in your question:
resultNotExpected == List(List(Map("1" -> "b"), Map("2" -> "b")))
The "keyPart"
In your question you mentioned the expected result as:
List(List(Map("1" -> "a", "2" -> "b"), Map("1" -> "b", "2" -> "a")))
I still don't understand the logic you have in mind for the now extracted keyPart function so I would give you mine... over-complex of course:
val resultExpected = list12.map { case (l1, l2) => List(l1, l2) }
.map(_.map(_.zipWithIndex)).map(_.zipWithIndex)
.map(_.map { case (l, i) => l.map { case ((k, v), j) => (k, v, i, j) } }).map(_.flatten)
.map(_.groupBy { case (k, v, i, j) => i == j }).map(_.values.toList)
.map(_.map(_.map { case (k, v, _, _) => k -> v }.sorted).sortBy(_.head._2))
.map(_.map(_.toMap))
scala> resultExpected == List(List(Map("1" -> "a", "2" -> "b"), Map("1" -> "b", "2" -> "a")))
Boolean = true
Perhaps you know better the logic to be implemented in keyPart.
In Scala, if we have List[(Char, Int)] ordered by the first element of each pair, does converting to a map via toMap preserve the ordering?
If so, does converting the resulting map back to list via toList preserve ordering?
No, Map (and Set) are not ordered. You can check this using Scala's interactive interpreter (REPL):
scala> val elems = List('a'->1, 'b'->2, 'c'->3, 'd'->4, 'e'->5)
elems: List[(Char, Int)] = List((a,1), (b,2), (c,3), (d,4), (e,5))
scala> elems.toMap.toList
res8: List[(Char, Int)] = List((e,5), (a,1), (b,2), (c,3), (d,4))
However, scala.collection does provide a SortedMap, which might be what you are looking for, though I haven't used this implementation yet.
Edit: Actually, there's an even more fundamental problem with this conversion: Because you cannot have duplicate keys in a map, you are not just losing guarantees on the ordering, but also, your list may have less elements after the conversion. Consider this:
scala> val elems = List('a'->1, 'a'->2)
elems: List[(Char, Int)] = List((a,1), (a,2))
scala> elems.toMap.toList
res9: List[(Char, Int)] = List((a,2))
As aforementioned, Map and Set have no ordering. Yet consider TreeMap which preserves ordering over its keys; for instance
val a = List(('z', 1), ('a', 4), ('b', 4))
a: List[(Char, Int)] = List((z,1), (a,4), (b,4))
val b = collection.immutable.TreeMap(a:_*)
b: scala.collection.immutable.TreeMap[Char,Int] = Map(a -> 4, b -> 4, z -> 1)
Update
Note that
for ( (k,v) <- b ) yield k
res: scala.collection.immutable.Iterable[Char] = List(a, b, z)
Maybe this might be easy to fix but can you help me out or guide me to a solution. I have a remove function that goes through a List of tuples "List[(String,Any)]" and im trying to replace the 1 index of the value with Nil when the list is being looped over.
But when I try to replace the current v with Nil, it say the v is assigned to "val". Now I understand that scala lists are immutable. So maybe this is what is going wrong?
I tried a Tail recursion implementation as will but when I get out of the def there is a type mismatch. ie: is unit but required: Option[Any]
// remove(k) removes one value v associated with key k
// from the dictionary, if any, and returns it as Some(v).
// It returns None if k is associated to no value.
def remove(key:String):Option[Any] = {
for((k,v) <- d){
if(k == key){
var temp:Option[Any] = Some(v)
v = Nil
return temp
}
}; None
}
Here was the other way of trying to figure out
def remove(key:String):Option[Any] = {
def removeHelper(l:List[(String,Any)]):List[(String,Any)] =
l match {
case Nil => Nil
case (k,v)::t => if (key == k) t else (k,v)::removeHelper(t)
}
d = removeHelper(d)
}
Any Suggestions? This is a homework/Project for school thought I might add that for the people that don't like to help with homework.
Well, there are many ways of answering that question. I'll be outlining the ones I can think of here with my own implementations, but the list is by no means exhaustive (nor, probably, the implementations optimal).
First, you can try with existing combinators - the usual suspects are map, flatMap, foldLeft and foldRight:
def remove_flatMap(key: String, list: List[(String, Any)]): List[(String, Any)] =
// The Java developer in me rebels against creating that many "useless" instances.
list.flatMap {a => if(a._1 == key) Nil else List(a)}
def remove_foldLeft(key: String, list: List[(String, Any)]): List[(String, Any)] =
list.foldLeft(List[(String, Any)]()) {(acc, a) =>
if(a._1 == key) acc
else a :: acc
// Note the call to reverse here.
}.reverse
// This is more obviously correct than the foldLeft version, but is not tail-recursive.
def remove_foldRight(key: String, list: List[(String, Any)]): List[(String, Any)] =
list.foldRight(List[(String, Any)]()) {(a, acc) =>
if(a._1 == key) acc
else a :: acc
}
The problem with these is that, as far as I'm aware, you cannot stop them once a certain condition has been reached: I don't think they solve your problem directly, since they remove all instances of key rather than the first.
You also want to note that:
foldLeft must reverse the list once it's done, since it appends elements in the "wrong" order.
foldRight doesn't have that flaw, but is not tail recursive: it will cause memory issues on large lists.
map cannot be used for your problem, since it only lets us modify a list's values but not its structure.
You can also use your own implementation. I've included two versions, one that is tail-recursive and one that is not. The tail-recursive one is obviously the better one, but is also more verbose (I blame the ugliness of using a List[(String, Any)] rather than Map[String, Any]:
def remove_nonTailRec(key: String, list: List[(String, Any)]): List[(String, Any)] = list match {
case h :: t if h._1 == key => t
// This line is the reason our function is not tail-recursive.
case h :: t => h :: remove_nonTailRec(key, t)
case Nil => Nil
}
def remove_tailRec(key: String, list: List[(String, Any)]): List[(String, Any)] = {
#scala.annotation.tailrec
def run(list: List[(String, Any)], acc: List[(String, Any)]): List[(String, Any)] = list match {
// We've been aggregating in the "wrong" order again...
case h :: t if h._1 == key => acc.reverse ::: t
case h :: t => run(t, h :: acc)
case Nil => acc.reverse
}
run(list, Nil)
}
The better solution is of course to use the right tool for the job: a Map[String, Any].
Note that I do not think I answer your question fully: my examples remove key, while you want to set it to Nil. Since this is your homework, I'll let you figure out how to change my code to match your requirements.
List is the wrong collection to use if any key should only exist once. You should be using Map[String,Any]. With a list,
You have to do extra work to prevent duplicate entries.
Retrieval of a key will be slower, the further down the list it appears. Attempting to retrieve a non-existent key will be slow in proportion to the size of the list.
I guess point 2 is maybe why you are trying to replace it with Nil rather than just removing the key from the list. Nil is not the right thing to use here, really. You are going to get different things back if you try and retrieve a non-existent key compared to one that has been removed. Is that really what you want? How much sense does it make to return Some(Nil), ever?
Here's a couple of approaches which work with mutable or immutable lists, but which don't assume that you successfully stopped duplicates creeping in...
val l1: List[(String, Any)] = List(("apple", 1), ("pear", "violin"), ("banana", Unit))
val l2: List[(Int, Any)] = List((3, 1), (4, "violin"), (7, Unit))
def remove[A,B](key: A, xs: List[(A,B)]) = (
xs collect { case x if x._1 == key => x._2 },
xs map { case x if x._1 != key => x; case _ => (key, Nil) }
)
scala> remove("apple", l1)
res0: (List[(String, Any)], List[(String, Any)]) = (List((1)),List((apple, List()),(pear,violin), (banana,object scala.Unit)))
scala> remove(4, l2)
res1: (List[(Int, Any)], List[(Int, Any)]) = (List((violin)),List((3,1), (4, List()), (7,object scala.Unit)))
scala> remove("snark", l1)
res2: (List[Any], List[(String, Any)]) = (List(),List((apple,1), (pear,violin), (banana,object scala.Unit)))
That returns a list of matching values (so an empty list rather than None if no match) and the remaining list, in a tuple. If you want a version that just completely removes the unwanted key, do this...
def remove[A,B](key: A, xs: List[(A,B)]) = (
xs collect { case x if x._1 == key => x._2 },
xs filter { _._1 != key }
)
But also look at this:
scala> l1 groupBy {
case (k, _) if k == "apple" => "removed",
case _ => "kept"
}
res3: scala.collection.immutable.Map[String,List[(String, Any)]] = Map(removed -> List((apple,1)), kept -> List((pear,violin), (banana,object scala.Unit)))
That is something you could develop a bit. All you need to do is add ("apple", Nil) to the "kept" list and extract the value(s) from the "removed" list.
Note that I am using the List combinator functions rather than writing my own recursive code; this usually makes for clearer code and is often as fast or faster than a hand-rolled recursive function.
Note also that I don't change the original list. This means my function works with both mutable and immutable lists. If you have a mutable list, feel free to assign my returned list as the new value for your mutable var. Win, win.
But please use a map for this. Look how simple things become:
val m1: Map[String, Any] = Map(("apple", 1), ("pear", "violin"), ("banana", Unit))
val m2: Map[Int, Any] = Map((3, 1), (4, "violin"), (7, Unit))
def remove[A,B](key: A, m: Map[A,B]) = (m.get(key), m - key)
scala> remove("apple", m1)
res0: (Option[Any], scala.collection.immutable.Map[String,Any]) = (Some(1),Map(pear -> violin, banana -> object scala.Unit))
scala> remove(4, m2)
res1: (Option[Any], scala.collection.immutable.Map[Int,Any]) = (Some(violin),Map(3 -> 1, 7 -> object scala.Unit))
scala> remove("snark", m1)
res2: res26: (Option[Any], scala.collection.immutable.Map[String,Any]) = (None,Map(apple -> 1, pear -> violin, banana -> object scala.Unit))
The combinator functions make things easier, but when you use the right collection, it becomes so easy that it is hardly worth writing a special function. Unless, of course, you are trying to hide the data structure - in which case you should really be hiding it inside an object.