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))
Related
I have some raw test data that I need to split into a map of format:
Map[String, List[(Int, String, Float)]]
I have managed to read in the data as a list and will give an example of one line of data below:
Oor Wullie Route (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
The above represents the following: A Route, a stage number:stage name:total distance of stage
So each set of 3 values (i.e 1:City Chambers:5) should be added to the [Int, String, Float] section of the map, with the route name being the key.
This is my code so far for reading the file and adding it to a list:
var mapBuffer: Map[String, List[(Int, String, Float)]] = Map()
val fitnessData = "C:\\Users\\josep\\Desktop\\Coursework\\Coursework\\src\\cw.txt"
val lines = Source.fromFile("C:\\Users\\josep\\Desktop\\Coursework\\Coursework\\src\\cw.txt").getLines.toList
I would like to write a funciton for splitting the data up and adding it to a map, essentially doing this:
var key ="Oor Wullie Route (GCU)"
var newList = List((1,"City Chambers",0.75f),(2,"Sir Chris Hoy Velodrome",3.8f),(3,"People's Palace",2.7f),(4,"Riverside Museum",5.4f),(5,"Botanic Gardens",2.4f),(6,"GCU",3.4f))
mapBuffer = mapBuffer ++ Map(key -> newList)
How can I add the data to a map in my desired format?
My suggestion would be to use foldLeft. Something like:
val resource = Source.fromFile("src/lines.txt")
val lines = resource.getLines.toList
resource.close()
val map = lines.foldLeft(Map[String, List[(Int, String, Float)]]())((map, line) => {
val keyValuesArray = line.split(",").toList
val key = keyValuesArray.head
val listOfValuesAsString = keyValuesArray.tail
val listOfValues = listOfValuesAsString.map {
case s"$integer:$string:$float" => (integer.toInt, string, float.toFloat)
}
map + (key -> listOfValues)
})
Start with empty map, and add key->values for each line.
Also, try match expressions when you parse data in list (listOfValues part is doing that).
This approach is with pattern matching and tail recursion.
I think it works very well.
First I convert the file into a List[Array[String]].
Second I call loop to go through the list in a recursive way and build the map.
Third inside the loop function I call make List to build the list of tuples in a recursive way.
As an example:
input
Oor Wullie Route (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
Oor Wullie Route2 (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
Oor Wullie Route3 (GCU),1:City Chambers:0.75f,2:Sir Chris Hoy Velodrome:3.8f,3:People's Palace:2.7f,4:Riverside Museum:5.4f,5:Botanic Gardens:2.4f,6:GCU:3.4f
code
import scala.io.Source
object ConverToMap {
#annotation.tailrec
def makeList(lst: List[String], acc: List[(Int, String, Float)]):List[(Int, String, Float)] = {
lst match {
case Nil => acc
case (h :: t) => {
val data = h.split(":")
val tuple = (data(0).toInt, data(1), data(2).substring(0,data(2).length - 1 ).toFloat)
makeList(t, tuple :: acc)
}
}
}
#annotation.tailrec
def loop(lst: List[Array[String]], acc: Map[String, List[(Int, String, Float)]]): Map[String, List[(Int, String, Float)]] = {
lst match {
case Nil => acc
case (h :: t) => {
val key = h(0)
val lTuple = makeList(h.toList.tail, List())
if(acc.contains(key)) loop(t, acc)
else loop(t, Map(key -> lTuple) ++ acc)
}
}
}
def main(args: Array[String]): Unit = {
val fitnessData = "/home/cloudera/files/tests/to_map.csv"
val lines = Source.fromFile(fitnessData)
.getLines
.toList
.map(line => line.split(","))
val mp = loop(lines, Map())
println(mp)
}
}
expected result
Map(Oor Wullie Route3 (GCU) -> List((6,GCU,3.4), (5,Botanic Gardens,2.4), (4,Riverside Museum,5.4), (3,People's Palace,2.7), (2,Sir Chris Hoy Velodrome,3.8), (1,City Chambers,0.7)),
Oor Wullie Route2 (GCU) -> List((6,GCU,3.4), (5,Botanic Gardens,2.4), (4,Riverside Museum,5.4), (3,People's Palace,2.7), (2,Sir Chris Hoy Velodrome,3.8), (1,City Chambers,0.7)),
Oor Wullie Route (GCU) -> List((6,GCU,3.4), (5,Botanic Gardens,2.4), (4,Riverside Museum,5.4), (3,People's Palace,2.7), (2,Sir Chris Hoy Velodrome,3.8), (1,City Chambers,0.7)))
I have a list (size of the list is variable):
val ids = List(7, 8, 9)
and would like to get the following map:
val result= Map("foo:7:bar" -> "val1",
"foo:8:bar" -> "val1",
"foo:9:bar" -> "val1")
everything in the map is hard-coded except ids and value is the same for everyone, but map has to be mutable, I'd like to update one of its values later:
result("foo:8:bar") = "val2"
val result= Map("foo:7:bar" -> "val1",
"foo:8:bar" -> "val2",
"foo:9:bar" -> "val1")
You can do that like this: First map over the list to produce a list of tuples, then call toMap on the result which will make an immutable Map out of the tuples:
val m = ids.map(id => ("foo:" + id + ":bar", "val1")).toMap
Then convert the immutable Map to a mutable Map, for example like explained here:
val mm = collection.mutable.Map(m.toSeq: _*)
edit - the intermediate immutable Map is not necessary as the commenters noticed, you can also do this:
val mm = collection.mutable.Map(ids.map(id => ("foo:" + id + ":bar", "val1")): _*)
Try this:
import scala.collection.mutable
val ids = List(7, 8, 9)
val m = mutable.Map[String, String]()
ids.foreach { id => m.update(s"foo:$id:bar", "val1") }
scala> m
Map(foo:9:bar -> val1, foo:7:bar -> val1, foo:8:bar -> val1)
You, don't need to create any intermediate objects, that map does.
Why not use foldLeft?
ids.foldLeft(mutable.Map.empty[String, String]) { (m, i) =>
m += (s"foo:$i:bar" -> "val1")
}
Try Like this:
scala> import scala.collection.mutable
scala> val result = mutable.Map[String, String]()
result: scala.collection.mutable.Map[String,String] = Map()
scala> val ids = List(7, 8, 9)
ids: List[Int] = List(7, 8, 9)
scala> for(x <- ids){
| result("foo:%d:bar".format(x)) = "val1"
| }
scala> result
res3: scala.collection.mutable.Map[String,String] = Map(foo:9:bar -> val1, foo:7:bar -> val1, foo:8:bar -> val1)
Lets take an example where store is my class.
class store(val a:Int) { }
In the code i want to create a list of store.
val list : List[store] = new List[store]()
How can i add a store in the same list?
First off, it's usually a good idea to capitalize your class names.
scala> class Store(val a:Int) { }
defined class Store
scala> val list : List[Store] = List.empty
list: List[Store] = List()
scala> val newList = new Store(4) :: list
newList: List[Store] = List(Store#243306de)
Your list is, by default, immutable so you'll have a new list every time an element is added.
scala> val newerList = new Store(71) :: newList
newerList: List[Store] = List(Store#30f57af0, Store#243306de)
ADDENDUM
If you need a mutable list (not usually recommended) you could try the following.
scala> import scala.collection.mutable.MutableList
import scala.collection.mutable.MutableList
scala> val myList: MutableList[Store] = MutableList.empty
myList: scala.collection.mutable.MutableList[Store] = MutableList()
scala> myList += new Store(56)
res322: myList.type = MutableList(Store#6421614e)
scala> myList += new Store(29)
res323: myList.type = MutableList(Store#6421614e, Store#85b26)
scala> myList += new Store(11)
res324: myList.type = MutableList(Store#6421614e, Store#85b26, Store#5d2f7883)
Mutable variables are considered poor style and an impediment to proper Functional Programming.
To add element to the beginning of the list use :: :
val l = List(1, 2, 3)
val l1 = 5 :: l // List(5, 1, 2, 3)
or
val l1 = l.::(5)
To add element to the end of the list use :+ :
val l2 = l :+ 5 // List(1, 2, 3, 5)
So, to add store object to the end of the list (though it is not efficient), write this:
val s = new Store(1)
val newList = list :+ s // list is immutable
i'm writing a method that take a list: List[(String, Int)] , x:(String, Int) and a n: Int parameters and return a List[(String, Int)]
The list parameter represents the input list, the x element represents the element to add to the list, the n represents the maximum dimension of the list.
The method has the following behavior:
First of all check if the list has n elements.
If list has
less than n elements, the method add the x elements to the list.
Otherwise if the list contain an elements less than x, the
method remove the minimum element from the list, and add the x
element to the list.
I've implemented the method as the following:
def method(list: List[(String, Int)], x: (String, Int), n: Int) = {
if(list.size < n)
list :+ x
else {
var index = -1
var min = 10000000//some big number
for(el <- 0 to list.size) {
if(list(el)._2 < x._2 && list(el)._2 < min) {
min = list(el)._2
index = el
}
}
if(index != -1) {
val newList = list.drop(index)
newList :+ x
}
else list
}
}
Exist a way to express this behavior in a more clean way??
First, a corrected version of what you posted (but it appears, not what you intended)
def method(list: List[(String, Int)], x: (String, Int), n: Int) = {
if(list.size < n)
list :+ x
else {
var index = -1
for(i <- 0 until list.size) {
val el = list(i)
if(el._2 < x._2)
index = i
}
if(index != -1) {
val (before, after) = list.splitAt(index)
val newList = before ++ after.tail
newList :+ x
}
else list
}
}
And some tests
val l1 = List(("one", 1))
val l2 = method(l1, ("three", 3), 2)
//> l2 : List[(String, Int)] = List((one,1), (three,3))
val l3 = method(l2, ("four", 4), 2)
//> l3 : List[(String, Int)] = List((one,1), (four,4))
val l4 = method(l2, ("zero", 0), 2)
//> l4 : List[(String, Int)] = List((one,1), (three,3))
Neater version (but still not meeting the spec as mentioned in a comment from the OP)
def method(list: List[(String, Int)], x: (String, Int), n: Int) = {
if (list.size < n)
list :+ x
else {
val (before, after) = list.span(_._2 >= x._2)
if (after.isEmpty)
list
else
before ++ after.tail :+ x
}
}
Another version that always removes the minimum, if that's less than x.
def method(list: List[(String, Int)], x: (String, Int), n: Int) = {
if (list.size < n)
list :+ x
else {
val smallest = list.minBy(_._2)
if (smallest._2 < x._2) {
val (before, after) = list.span(_ != smallest)
before ++ after.tail :+ x
} else
list
}
}
and its test results
val l1 = List(("one", 1))
val l2 = method(l1, ("three", 3), 2)
//> l2 : List[(String, Int)] = List((one,1), (three,3))
val l3 = method(l2, ("four", 4), 2)
//> l3 : List[(String, Int)] = List((three,3), (four,4))
val l4 = method(l2, ("zero", 0), 2)
//> l4 : List[(String, Int)] = List((one,1), (three,3))
but as I say in a comment, better to use a method that processes the whole sequence of Xs and returns the top N.
I would separate the ordering from the method itself:
def method[A: Ordering](list: List[A], x: A, n: Int) = ...
What do you want to do if the list contains more than one element less than x? If you're happy with replacing all of them, you could use map rather than your handwritten loop:
list map {
case a if Ordering.lt(a, x) => x
case a => a
}
If you only want to replace the smallest element of the list, maybe it's neater to use min?
val m = list.min
if(m < x) {
val (init, m :: tail) = list.splitAt(list.indexOf(m))
init ::: x :: tail
else list
For
trait Item
case class TypeA(i: Int) extends Item
case class TypeB(i: Int) extends Item
consider a Scala list of items such as
val myList = List(TypeA(1), TypeB(11), TypeB(12),
TypeA(2), TypeB(21),
TypeA(3), TypeB(31))
The goal is to define a new slice method that can be applied onto myList and which takes a predicate or condition as argument; for instance
myList.slice { x => x.isInstanceOf[TypeA] }
would deliver
List(List(TypeA(1), TypeB(11), TypeB(12)),
List(TypeA(2), TypeB(21)),
List(TypeA(3), TypeB(31)))
In this example, an identical result would be achieved by
myList.slice { case TypeA(x) => x < 10 }
Many Thanks.
List already has a slice method - it takes a subset of elements between a start and end index. What you're looking for is repeated application of the span method:
def span(p: (A) ⇒ Boolean): (List[A], List[A])
Which is documented as:
Splits this list into a prefix/suffix pair according to a predicate.
Note: c span p is equivalent to (but possibly more efficient than) (c takeWhile p, c dropWhile p), provided the evaluation of the predicate p does not cause any side-effects.
returns: a pair consisting of the longest prefix of this list whose elements all satisfy p, and the rest of this list.
You can get what you need by repeatedly using this method with an inverse predicate, and an extra bit of logic to ensure that none of the returned Lists are empty.
import annotation.tailrec
def multiSpan[A](xs: List[A])(splitOn: (A) => Boolean): List[List[A]] = {
#tailrec
def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
case Nil => acc
case x :: Nil => List(x) :: acc
case h :: t =>
val (pre,post) = t.span(!splitOn(_))
loop(post, (h :: pre) :: acc)
}
loop(xs, Nil).reverse
}
UPDATE
As requested in comments on the original post, here's a version that enriches list instead of being a standalone method:
implicit class AddMultispanToList[A](val list: List[A]) extends AnyVal {
def multiSpan(splitOn: (A) => Boolean): List[List[A]] = {
#tailrec
def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
case Nil => acc
case x :: Nil => List(x) :: acc
case h :: t =>
val (pre,post) = t.span(!splitOn(_))
loop(post, (h :: pre) :: acc)
}
loop(list, Nil).reverse
}
}
Use as:
myList.multiSpan(_.isInstanceOf[TypeA])
Why couldn't you use partition method from the standard API?
example:
scala> val l = List(3,5,4,6)
l: List[Int] = List(3, 5, 4, 6)
scala>
scala> val (odd,even) = l.partition(_ %2 ==1)
odd: List[Int] = List(3, 5)
even: List[Int] = List(4, 6)
For your example:
scala> val (typeA,typeB) = myList.partition(_.isInstanceOf[TypeA])
typeA: List[Product with Serializable with Item] = List(TypeA(1), TypeA(2), TypeA(3))
typeB: List[Product with Serializable with Item] = List(TypeB(11), TypeB(12), TypeB(21), TypeB(31))
Aren't you looking for filter, which works (almost) without any tweaks for your examples?
$ sbt console
scala> trait Item
scala> case class TypeA(i: Int) extends Item
scala> case class TypeB(i: Int) extends Item
scala> val myList = List(TypeA(1), TypeB(11), TypeB(12),
TypeA(2), TypeB(21),
TypeA(3), TypeB(31))
myList: List[Product with Serializable with Item] = List(TypeA(1), TypeB(11), TypeB(12), TypeA(2), TypeB(21), TypeA(3), TypeB(31))
your first works unaltered:
scala> myList.filter { x => x.isInstanceOf[TypeA] }
res0: List[Product with Serializable with Item] = List(TypeA(1), TypeA(2), TypeA(3))
your second requires a default case:
scala> myList.filter { case TypeA(x) => x < 10; case _ => false }
res2: List[Product with Serializable with Item] = List(TypeA(1(3))
See also collect, which takes a partial function instead of a boolean predicate:
scala> myList.collect { case z # TypeA(x) if x < 10 => z }
res3: List[TypeA] = List(TypeA(1), TypeA(2), TypeA(3))
and can transform as well:
scala> myList.collect { case TypeA(x) if x < 10 => x }
res4: List[Int] = List(1, 2, 3)