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)
Related
I've a two lists dest (contains:x) and points (x,y)
dest:List[Int] and Points:List[(Int,Int)]
I want to filter elements in the dest, if it exists in points (x==points._1) i
var newl:List[Int] = List()
for(x<-dest) if(!points.filter(_._1==x).isEmpty) newl=newl:+x
I feel like there must be a better concise way with exists but tuple making it complex. So whats the best way to do the above?
Here is a concise way:
val dest= List(1,2,4,5)
val points = List((1,3), (2,3) , (3,4))
val newl = dest.filter{d => points.exists(_._1 == d)} // returns List(1, 2)
The following is even better order of complexity wise:
val dest= List(1,2,4,5)
val points = List((1,3), (2,3) , (3,4))
val xs = points.map{_._1}.toSet
val newl = dest.filter(xs.contains(_))
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))
I'd like to build the following list of tuples:
List(("a", 1), ("a", 2), ("a", 3), ("a", 4))
from two components:
"a"
List(1,2,3,4)
Known workarounds include:
List(1,2,3,4) zip Stream.continually("a") map { _.swap }
List(1,2,3,4).zipAll("a", "for missing values", "a")
List(1,2,3,4).map(("a",_))
However, I believe there is a better solution than these workarounds.
What's your definition of "better"?
Here is some possible solution:
scala> (List.fill(4)("a"), List(1, 2, 3, 4)).invert
res0: List[(String, Int)] = List((a,1), (a,2), (a,3), (a,4))
scala> List(1, 2, 3, 4).map{ ("a", _) }
res1: List[(String, Int)] = List((a,1), (a,2), (a,3), (a,4))
Just tried on a Scala worksheet:
List(1,2,3,4) map (v => ("a", v)) //> res0: List[(String, Int)] = List((a,1), (a,2), (a,3), (a,4))
What's wrong with it?
AFAIK there is no direct implementation of an inverted zip method in the standard Scala collection library.
However, if you take a look at the implementation of zip, which List takes from IterableLike, you can easily define your own zipInvert extension method:
import language.higherKinds
import collection.GenIterable
import collection.generic.CanBuildFrom
implicit class ZipInvert[A,CC[X] <: GenIterable[X]](coll: CC[A]) {
def zipInvert[A1 >: A, B, That](that: GenIterable[B])(implicit bf: CanBuildFrom[CC[A], (B, A1), That]): That = {
val b = bf(coll)
val these = coll.iterator
val those = that.iterator
while (these.hasNext && those.hasNext)
b += ((those.next, these.next))
b.result
}
}
val numberList = List.range(1,5)
numberList zipInvert Stream.continually("a")
Paste this into the REPL to execute it or paste it in a file and execute it with scala -i:
scala -i ZipInvert.scala
Loading ZipInvert.scala...
import language.higherKinds
import collection.GenIterable
import collection.generic.CanBuildFrom
defined class ZipInvert
numberList: List[Int] = List(1, 2, 3, 4)
res0: List[(String, Int)] = List((a,1), (a,2), (a,3), (a,4))
...
The example above is compiled with Scala 2.10.4.
If you want to have no intermediate collection at all, e.g. to avoid further memory overhead, use the List companion object:
$ scala
Welcome to Scala version 2.10.4 (OpenJDK 64-Bit Server VM, Java 1.7.0_51).
Type in expressions to have them evaluated.
Type :help for more information.
scala> List.tabulate(4)(index => ("a",index+1))
res0: List[(String, Int)] = List((a,1), (a,2), (a,3), (a,4))
scala>
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.
I'm new to scala and I'm trying to remove from a list of tuples elements which their first value is bigger than the second.
For example, From the list:
val list = List[(Int,Int)]((1,3),(3,1),(2,2))
I want to get the list:
val list = List[(Int,Int)]((1,3),(2,2))
So I used the following lines:
var newList = List[(Int, Int)]()
for (element <- list) {
if (element._1 <= element._2) {
newList ::= element;
}
}
But it feels very long for scala.. Is there a shorter way?
Like twillouer's and tzofia's solutions, but with pattern matching:
list filter { case (a, b) => a <= b }
You can simply do:
list.filter(element => element._1 <= element._2)
The filter function filters out elements which do not satisfy the given boolean condition.
you can use filter like this :
scala> val list = List[(Int,Int)]((1,3),(3,1),(2,2))
list: List[(Int, Int)] = List((1,3), (3,1), (2,2))
scala> val newList = list.filter(a => a._1 <= a._2)
newList: List[(Int, Int)] = List((1,3), (2,2))
or filterNot for the example :
scala> val newList = list.filterNot(a => a._1 > a._2)
newList: List[(Int, Int)] = List((1,3), (2,2))