scala: list as input and output of function - scala

I am new to scala. I have a very simple problem.
Given a list in python
x=[1, 100, "a1", "b1"]
I can write a function that will return the last two elements
def f(w):
if w[0]>=1 and w[1]<=100:
return ([w[2],w[3]])
How do I do the equivalent in scala
val v= List(1, 100, "a1", "b1")
def g(L:List[Any]): List[String] = {
if( L(0)>=1 & L(1)<=100 ) {return List(L(2), L(3))}
}
val w=g(v)
This gets me the error
List[Any] = List(1, 100, a, b)
Incomplete expression

You can't get a List[String] from a List[Any]. (Well, you can, but it's a really bad thing to do.)
Don't, don't, don't create a List[Any]. Unlike Python, Scala is a strictly typed language, which means that the compiler keeps a close watch on the type of each variable and every collection. When the compiler looses track of the List type it becomes List[Any] and you've lost all the assistance the compiler offers to help write programs that don't crash.
To mix types in a collection you can use tuples. Here's the type-safe Scala way to write your g() method.
def g(tup: (Int,Int,String,String)): List[String] =
if (tup._1 >= 1 & tup._2 <= 100) List(tup._3, tup._4)
else List()
Usage:
val v = (1, 100, "a1", "b1")
val w = g(v) //w: List[String] = List(a1, b1)

It seems like you have a typo here:
if(L(0)>=1 & L(1<=100)) {return List(L(2), L(3))}
Wouldn't it be like this?
if(L(0)>=1 & L(1)<=100) {return List(L(2), L(3))}
The error seems to point out there's something wrong with that extra bracket there.

scala> List(1,2,3,4,5).takeRight(2)
res44: List[Int] = List(4, 5)
You can use a built in function in Scala that does this!

Related

Indexing/Slicing of Collections in Scala

I saw some really cool methods to operate on Scala Collections but I wanted to know how can one do the slicing operation in Scala? I see methods like dropLeft take but curious to know if something simpler like indexing or slice exists in Scala.
For example:
val aString = "I want this word"
val aList = List(1,2,3,4)
should return:
val slicedString = aString.slice(7,11) => "this" //JavaScript type
and
val slicedList = aList.slice(0,2) => List(1,2) //JavaScript type
or indexing like how it's done in python:
val slicedString = aString(7:11) => "this"
val slicedList = aList(0:2) => List(1,2)
Had you bothered to consult the ScalaDocs you would have found what you're looking for.
aString.slice(7,11) //res0: String = this
aList.slice(0,2) //res1: List[Int] = List(1, 2)

Replace elements in List by condition

I have a pretty much large val s: List[Int] = //..., a function f: Int => Boolean and a function transform: Int => Int.
The problem: I want to create another List[Int] such that all elements e: Int of the s: List[Int] such that f(e) = true are replaced with transform(e).
I looked at cats-mtl FunctorEmpty (to adhere to functional programming style), but it does not seem to work in my case. Maybe some cats/scalaz data structures can be useful here? Or any other way?
s.map{ e => if(f(e)) transform(e) else e }
List(1, 2, 3).map(fn) creates a new list which might be not exactly want you need, especially if input is large.
Alternative solution would be to map on view List(1, 2, 3).view.map(...) without creating a new list and only "materialize" results when you need them

Scala collections: Is there a safe map operation?

Let's say that I have a List (or the values in a Map), and i want to perform an operation on each item. But unfortunately, for whatever reason, this list of values can contain nulls.
scala> val players = List("Messi", null, "Xavi", "Iniesta", null)
players: List[java.lang.String] = List(Messi, null, Xavi, Iniesta, null)
In order to avoid blowing up with a NPE, i need to do the following:
scala> players.filterNot(_ == null ).map(_.toUpperCase)
res84: List[java.lang.String] = List(MESSI, XAVI, INIESTA)
Is there any better way of doing this?
Ideally something like:
players.safeMap(_.toUpperCase)
On the scala-language mailing list, Simon proposed this:
players.filter ( null !=).map(_.toUpperCase )
which is shorter version of my original take, and as short as you can get without a dedicated method.
Even better, Stefan and Kevin proposed the method withFilter which will return a lazy proxy, so both operations can be merged.
players.withFilter ( null !=).map(_.toUpperCase )
If you can’t avoid nulls (e.g. if you get your list from Java code), another alternative is to use collect instead of map:
scala> players.collect { case player if player != null => player.toUpperCase }
res0: List[java.lang.String] = List(MESSI, XAVI, INIESTA)
I'd do this:
players flatMap Option map (_.toUpperCase)
But that's worse than collect. filter + map is always better done with collect.
You could convert to a list of Option[String]:
scala> val optionPlayers = players.map(Option(_))
optionPlayers: List[Option[java.lang.String]] = List(Some(Messi), None, Some(Xavi), Some(Iniesta), None)
Option is universally preferred to null and it gives you a lot of flexibility in how you can safely handle the data. Here's are thee easy ways to get the result you were looking for:
scala> optionPlayers.collect { case Some(s) => s.toUpperCase }
res0: List[java.lang.String] = List(MESSI, XAVI, INIESTA)
scala> optionPlayers.flatMap(_.map(_.toUpperCase))
res1: List[java.lang.String] = List(MESSI, XAVI, INIESTA)
scala> optionPlayers.flatten.map(_.toUpperCase)
res2: List[java.lang.String] = List(MESSI, XAVI, INIESTA)
You can find a lot more information about Option in other StackOverflow questions or by searching the web.
Or, you can always just define that safeMap method you wanted as an implicit on List:
implicit def enhanceList[T](list: List[T]) = new {
def safeMap[R](f: T => R) = list.filterNot(_ == null).map(f)
}
so you can do:
scala> players.safeMap(_.toUpperCase)
res4: List[java.lang.String] = List(MESSI, XAVI, INIESTA)
Though if you define an implicit, you might want to use a CanBuildFrom style like the basic collections do to make it work on more than just List. You can find more information about that elsewhere.

Strange behavior with reduce and fold on identical code block

EDIT
Ok, #dhg discovered that dot-method syntax required if the code block to fold() is not bound to a val (why with reduce() in the same code block one can use space-method syntax, I don't know). At any rate, the end result is the nicely concise:
result.map { row =>
addLink( row.href, row.label )
}.fold(NodeSeq.Empty)(_++_)
Which negates to some degree the original question; i.e. in many cases one can higher-order away either/or scenarios and avoid "fat", repetitive if/else statements.
ORIGINAL
Trying to reduce if/else handling when working with possibly empty collections like List[T]
For example, let's say I need to grab the latest news articles to build up a NodeSeq of html news <li><a>links</a></li>:
val result = dao.getHeadlines // List[of model objects]
if(result.isEmpty) NodeSeq.Empty
else
result map { row =>
addLink( row.href, row.label ) // NodeSeq
} reduce(_ ++ _)
This is OK, pretty terse, but I find myself wanting to go ternary style to address these only-will-ever-be either/or cases:
result.isEmpty ? NodeSeq.Empty :
result map { row =>
addLink( row.href, row.label )
} reduce(_ ++ _)
I've seen some old postings on pimping ternary onto boolean, but curious to know what the alternatives are, if any, to streamline if/else?
match {...} is, IMO, a bit bloated for this scenario, and for {...} yield doesn't seem to help much either.
You don't need to check for emptiness at all. Just use fold instead of reduce since fold allows you to specify a default "empty" value:
scala> List(1,2,3,4).map(_ + 1).fold(0)(_+_)
res0: Int = 14
scala> List[Int]().map(_ + 1).fold(0)(_+_)
res1: Int = 0
Here's an example with a List of Seqs:
scala> List(1,2).map(Seq(_)).fold(Seq.empty)(_++_)
res14: Seq[Int] = List(1, 2)
scala> List[Int]().map(Seq(_)).fold(Seq.empty)(_++_)
res15: Seq[Int] = List()
EDIT: Looks like the problem in your sample has to do with the dropping of dot (.) characters between methods. If you keep them in, it all works:
scala> List(1,2,3).map(i => node).fold(NodeSeq.Empty)(_ ++ _)
res57: scala.xml.NodeSeq = NodeSeq(<li>Link</li>, <li>Link</li>, <li>Link</li>)

Returning an element from a List in Scala

I've recently been working on a beginner's project in Scala, and have a beginner question about Scala's Lists.
Say I have a list of tuples ( List[Tuple2[String, String]], for example). Is there a convenience method to return the first occurence of a specified tuple from the List, or is it necessary to iterate through the list by hand?
scala> val list = List(("A", "B", 1), ("C", "D", 1), ("E", "F", 1), ("C", "D", 2), ("G", "H", 1))
list: List[(java.lang.String, java.lang.String, Int)] = List((A,B,1), (C,D,1), (E,F,1), (C,D,2), (G,H,1))
scala> list find {e => e._1 == "C" && e._2 == "D"}
res0: Option[(java.lang.String, java.lang.String, Int)] = Some((C,D,1))
You could try using find. (Updated scala-doc location of find)
As mentioned in a previous comment, find is probably the easiest way to do this. There are actually three different "linear search" methods in Scala's collections, each returning a slightly different value. Which one you use depends upon what you need the data for. For example, do you need an index, or do you just need a boolean true/false?
If you're learning scala, I'd take a good look at the Seq trait. It provides the basis for much of scala's functional goodness.
You could also do this, which doesn't require knowing the field names in the Tuple2 class--it uses pattern matching instead:
list find { case (x,y,_) => x == "C" && y == "D" }
"find" is good when you know you only need one; if you want to find all matching elements you could either use "filter" or the equivalent sugary for comprehension:
for ( (x,y,z) <- list if x == "C" && y == "D") yield (x,y,z)
Here's code that may help you.
I had a similar case, having a collection of base class entries (here, A) out of which I wanted to find a certain derived class's node, if any (here, B).
class A
case class B(val name: String) extends A
object TestX extends App {
val states: List[A] = List( B("aa"), new A, B("ccc") )
def findByName( name: String ): Option[B] = {
states.find{
case x: B if x.name == name => return Some(x)
case _ => false
}
None
}
println( findByName("ccc") ) // "Some(B(ccc))"
}
The important part here (for my app) is that findByName does not return Option[A] but Option[B].
You can easily modify the behaviour to return B instead, and throw an exception if none was found. Hope this helps.
Consider collectFirst which delivers Some[(String,String)] for the first matching tuple or None otherwise, for instance as follows,
xs collectFirst { case t#(a,_) if a == "existing" => t }
Some((existing,str))
scala> xs collectFirst { case t#(a,_) if a == "nonExisting" => t }
None
Using # we bind the value of the tuple to t so that a whole matching tuple can be collected.