Just wondering if anyone would know how to make the following if statement more functional:
val value1 = 8
val value2 = 10
if(val1 > val2){
println("\n" + stock1 + " has the highest current value")
}else if(val1 < val2){
println("\n" + stock2 + " has the highest current value")
}else if(val1 == val2)){
println("\nThe current value for " + stock1 + " is equal the current value for " + stock2)
}
I would be very grateful if someone could make a suggestion.
Thanks
Using cats-kernel you could use Order[Int].comparison :
import cats.kernel.Order
import cats.kernel.instances.int._
import cats.kernel.Comparison._
val message =
Order[Int].comparison(val1, val2) match {
case GreaterThan => s"$stock1 has the highest current value"
case LessThan => s"$stock2 has the highest current value"
case EqualTo =>
s"The current value for $stock1 is equal the current value for $stock2"
}
println(s"\n$message")
match, if you like it
val1 match{
case v if v > val2 => println("\n" + stock1 + " has the highest current value")
case v if v < val2 => println("\n" + stock2 + " has the highest current value")
case _ => println("\nThe current value for " + stock1 + " is equal the current value for " + stock2)
}
I think the key thing about being functional here is to separate calculation from effects.
Your snippet is both calculating the message to print and doing IO. There's nothing wrong with using if statements -- in this case I'd say it's the clearest way to write it.
val value1 = 8
val value2 = 10
def valueMessage =
if (val1 > val2) stock1 + " has the highest current value"
else if (val1 < val2) stock2 + " has the highest current value"
else "The current value for " + stock1 + " is equal the current value for " + stock2
def printValueMessage() = println("\n" + valueMessage)
printValueMessage() // unsafe
Or this:
math.signum(value1 - value2) match {
case -1 => println("v1 <= v2")
case 1 => println("v1 >= v2")
case _ => println("v1 == v2")
}
Like this? What do you think is not functional enough about your solution?
println(
if (val1 > val2)
s"\n$stock1 has the highest current value"
else if (val1 < val2)
s"\n$stock2 has the highest current value"
else
s"\nThe current value for $stock1 is equal the current value for $stock2"
)
Related
I am quite new to Scala and functional programming.
I wrote the simple codes as below, which manipulates the string by counting the word.
When the 4th comma-delimitted part is empty then, I concated only three columns, otherwise I concated all the columns including the values as code above.
But I think that it is not quite proper to the functional programming. Because I used the if statement to see the input value contains the value or not.
How to change it to the more scala-like code?
str = "aa,bb,1668268540040,34.0::aa,bb,1668268540040"
val parts = str.split("::")
for (case <- parts) {
val ret = case.map(c => if (c.value.isEmpty) {
c.columnFamily + "," + c.qualifier + "," + c.ts
} else {
c.columnFamily + "," + c.qualifier + "," + c.ts + "," + c.value
})
}
str = "aa,bb,1668268540040,34.0::aa,bb,166826434343"
val parts = str.split("::")
for (part <- parts) {
val elem = part.split(",", 4)
if (elem.length == 4) {
val Array(f, q, t, v) = elem
state.put(f + ":" + q, (v, t.toLong))
} else {
val Array(f, q, t) = elem
state.put(f + ":" + q, ("", t.toLong))
}
}
#LeviRamsey's comment tells you actually everything, but just to make your code more "scala-ish", you should avoid mutable data structures in the first place (what you're doing with state, which I think is a Map object), and use immutable data structures. About your if-else part, it's actually okay in FP, but in Scala, you can use pattern matching on a list, rather than manual length checking and using Arrays. Something like this:
parts.foldLeft(Map.empty[String, (String, Long)]) {
case (state, part) =>
part.split(",", 4).toList match {
case f :: q :: t :: v :: Nil =>
state.updated(f + ":" + q, (v, t.toLong))
case f :: q :: t :: Nil =>
state.updated(f + ":" + q, ("", t.toLong))
case _ => state // or whatever thing you want to do, in case neither 4 nor 3 elements are splitted
}
}
while performing a hands on code practice i am facing cannot resolve symbol x from intellj
error code line
println(lst.reduceLeft((x,y) => {println(x + " , "+ y) x +y}))
println(lst.reduceRight((x,y) => {println(x + " , "+ y) x -y}))
i have tried to debugg from the suggestions from intellj but not working
Intellj
Build #IC-221.5591.52, built on May 10, 2022
scala version
scala-sdk-2.11.12
reference
http://www.codebind.com/scala/scala-reduce-fold-scan-leftright/?unapproved=192475&moderation-hash=8cdabb0f7834cbe19792b863eb952538#comment-192475
//In Scala Reduce, fold or scan are most commonly used with collections in the form of reduceLeft, reduceRight, foldLeft, foldRight, scanLeft or scanRight.
// In general, all functions apply a binary operator to each element of a collection.
// The result of each step is passed on to the next step.
package pack {
}
object obj2 {
println
println("=====started=======")
println
//val is a constant (which is an un changeable variable),A variable holds a value / address to a value in memory
val lst = List(1, 2, 3, 5, 7, 10, 13)
val lst2 = List("A", "B", "C")
def main(args: Array[String]) {
println(lst.reduceLeft(_ + _))
println(lst2.reduceLeft(_ + _))
println(lst.reduceLeft((x,y) => {println(x + " , "+ y) x +y}))
println(lst.reduceLeft(_ - _))
println(lst.reduceRight(_ - _))
println(lst.reduceRight((x,y) => {println(x + " , "+ y) x -y}))
println(lst.foldLeft(100)(_ + _))
println(lst2.foldLeft("z")(_ + _))
println(lst.scanLeft(100)(_ + _))
println(lst2.scanLeft("z")(_ + _))
}
}
println(lst.reduceLeft((x,y) => { println(x + " , " + y) x + y }))
The code inside the { } is not a valid expression. It looks like you are expecting Scala to work out that there are two expressions here, but it can't. You need to put in an explicit ; to fix it:
println(lst.reduceLeft((x,y) => { println(x + " , " + y); x + y }))
println(lst.reduceLeft((x,y) => {println(x + " , "+ y) x +y})) is not valid. If you want several instructions in a anonymous function you need either to separate them with ; or make it multiline:
println(lst.reduceLeft((x, y) => { println(x + " , " + y); x + y }))
or
println(lst.reduceLeft((x, y) => {
println(x + " , " + y)
x + y
}))
Also better use string interpolation instead of concatenation:
println(lst.reduceLeft((x, y) => {
println(s"$x , $y")
x + y
}))
I have a small problem. I would like to delete any row that contains 'NULL'.
This is my input file:
matricule,dateins,cycle,specialite,bourse,sport
0000000001,1999-11-22,Master,IC,Non,Non
0000000002,2014-02-01,Null,IC,Null,Oui
0000000003,2006-09-07,Null,Null,Oui,Oui
0000000004,2008-12-11,Master,IC,Oui,Oui
0000000005,2006-06-07,Master,SI,Non,Oui
I did a lot of research and found a function called drop(any). Which basically drops any rows that contains NULL value. I tried using it in the code below but it wont work
val x = sc.textFile("/home/amel/one")
val re = x.map(row => {
val cols = row.split(",")
val cycle = cols(2)
val years = cycle match {
case "License" => "3 years"
case "Master" => "3 years"
case "Ingeniorat" => "5 years"
case "Doctorate" => "3 years"
case _ => "other"
}
(cols(1).split("-")(0) + "," + years + "," + cycle + "," + cols(3), 1)
}).reduceByKey(_ + _)
re.collect.foreach(println)
This is the current result of my code:
(1999,3 years,Master,IC,57)
(2013,NULL,Doctorat,SI,44)
(2013,NULL,Licence,IC,73)
(2009,5 years,Ingeniorat,Null,58)
(2011,3 years,Master,Null,61)
(2003,5 years,Ingeniorat,Null,65)
(2019,NULL,Doctorat,SI,80)
However, I want the result to be like this:
(1999, 3 years, Master, IC)
I.e., any row that contains 'NULL' should be removed.
Similar but not duplicate question as the following question on SO: Filter spark DataFrame on string contains
You can filter this RDD when you read it in.
val x = sc.textFile("/home/amel/one").filter(!_.toLowerCase.contains("null"))
I'm trying to find the min distance between multiple words in a given text.
Let's suppose I a string such as: "a b cat dog x y z n m p fox x dog b b cat"
Find the min distance of all matches of substrings: (fox, dog, cat)
there are multiple occurrences of each substring in this text:
one at the beginning:
cat - 4
dog - 8
fox - 24
dist = 24 - 4 = 20
And one at the end of string:
fox - 24
dog - 30
cat - 38
dist = 38 - 24 = 14
the min Dist = 14
This is the algorithm I came up with:
object MinKWindowSum {
def main(args: Array[String]): Unit = {
val document =
"""This Hello World is a huge text with thousands
Java of Hello words and Scala other lines and World and many other Hello docs
Words of World in many langs Hello and features
Java Scala AXVX TXZX ASDQWE OWEQ World asb eere qwerer
asdasd Scala Java Hello docs World KLKM NWQEW ZXCASD OPOOIK Scala ASDSA
"""
println(getMinWindowSize(document, "Hello World Scala"))
}
def getMinWindowSize(str:String, s:String): Int = {
/* creates a list of tuples List[(String, Int)] which contains each keyword and its
respective index found in the text sorted in order by index.
*/
val keywords = s.split(" ").toSet
val idxs = keywords.map(k => (k -> ("(?i)\\Q" + k + "\\E").r.findAllMatchIn(str).map(_.start)))
.map{ case (keyword,itr) => itr.map((keyword, _))}
.flatMap(identity).toSeq
.sortBy(_._2)
// Calculates the min window on the next step.
var min = Int.MaxValue
var minI, minJ = -1
// current window indexes and words
var currIdxs = ListBuffer[Int]()
var currWords = ListBuffer[String]()
for(idx <- idxs ) {
// check if word exists in window already
val idxOfWord = currWords.indexOf(idx._1)
if (!currWords.isEmpty && idxOfWord != -1) {
currWords = currWords.drop(idxOfWord + 1)
currIdxs = currIdxs.drop(idxOfWord + 1)
}
currWords += idx._1
currIdxs += idx._2
// if all keys are present check if it is new min window
if (keywords.size == currWords.length) {
val currMin = Math.abs(currIdxs.last - currIdxs.head)
if (min > currMin) {
min = currMin
minI = currIdxs.head
minJ = currIdxs.last
}
}
}
println("min = " + min + " ,i = " + minI + " j = " + minJ)
min
}
}
In the example above we try to find the min distance between all matches of "Hello World Scala"
The shortest window between the indexes is found between indexes:
i = 235, j = 257 --> min = 22
Was wondering if there is a better way of doing this in an idiomatic way or in a better manner in terms of efficiency, scalability, readability and simplicity?
Here is a slightly "more functional" alternative:
val document =
"""This Hello World is a huge text with thousands Java of Hello words and Scala other lines and World and many other Hello docs
Words of World in many langs Hello and features Java Scala AXVX TXZX ASDQWE OWEQ World
"""
val WORDS = Set("Hello", "World", "Scala")
var minDistance = document.trim
.split(" ")
.foldLeft(List[(String, Int)](), None: Option[Int], 0) {
case ((words, min, idx), word) if WORDS.contains(word) =>
val newWords = (word, idx) :: words.filter(_._1 != word)
if (newWords.map(_._1).toSet == WORDS) { // toSet on only 3 elmts
var idxes = newWords.map(_._2)
var dist = idxes.max - idxes.min
var newMin = min match {
case None => dist
case Some(min) if min < dist => min
case _ => dist
}
(newWords, Some(newMin), idx + word.length + 1)
}
else {
(newWords, min, idx + word.length + 1)
}
case ((words, min, idx), word) =>
(words, min, idx + word.length + 1)
}
._2
println(minDistance)
which produces:
Some(38)
My approach starts with a similar premise but uses a tail-recursive helper method to search the indexed words.
def getMinWindowSize(str :String, s :String) :Int = {
val keywords = s.split("\\s+").toSet
val re = "(?i)\\b(" + keywords.mkString("|") + ")\\b"
val idxs = re.r.findAllMatchIn(str).map(w => w.start -> w.toString).toList
def dist(input :List[(Int, String)], keys :Set[String]) :Option[Int] = input match {
case Nil => None
case (idx, word) :: rest =>
if (keys(word) && keys.size == 1) Some(idx)
else dist(rest, keys diff Set(word))
}
idxs.tails.collect{
case (idx, word)::rest => dist(rest, keywords diff Set(word)).map(_ - idx)
}.flatten.reduceOption(_ min _).getOrElse(-1)
}
No mutable variables or data structures. I also used Option to help return a more meaningful value if no minimum window is possible.
Usage:
getMinWindowSize(document, "Hello World Scala") //res0: Int = 22
getMinWindowSize(document, "Hello World Scal") //res1: Int = -1
I have some tests with results that I can't quite explain.
The first test does a filter, map and reduce on a list containing 4 elements:
{
val counter = new AtomicInteger(0)
val l = List(1, 2, 3, 4)
val filtered = l.filter{ i =>
counter.incrementAndGet()
true
}
val mapped = filtered.map{ i =>
counter.incrementAndGet()
i*2
}
val reduced = mapped.reduce{ (a, b) =>
counter.incrementAndGet()
a+b
}
println("counted " + counter.get + " and result is " + reduced)
assert(20 == reduced)
assert(11 == counter.get)
}
The counter is incremented 11 times as I expected: once for each element during filtering, once for each element during mapping and three times to add up the 4 elements.
Using wildcards the result changes:
{
val counter = new AtomicInteger(0)
val l = List(1, 2, 3, 4)
val filtered = l.filter{
counter.incrementAndGet()
_ > 0
}
val mapped = filtered.map{
counter.incrementAndGet()
_*2
}
val reduced = mapped.reduce{ (a, b) =>
counter.incrementAndGet()
a+b
}
println("counted " + counter.get + " and result is " + reduced)
assert(20 == reduced)
assert(5 == counter.get)
}
I can't work out how to use wildcards in the reduce (code doesnt compile), but now, the counter is only incremented 5 times!!
So, question #1: Why do wildcards change the number of times the counter is called and how does that even work?
Then my second, related question. My understanding of views was that they would lazily execute the functions passed to the monadic methods, but the following code doesn't show that.
{
val counter = new AtomicInteger(0)
val l = Seq(1, 2, 3, 4).view
val filtered = l.filter{
counter.incrementAndGet()
_ > 0
}
println("after filter: " + counter.get)
val mapped = filtered.map{
counter.incrementAndGet()
_*2
}
println("after map: " + counter.get)
val reduced = mapped.reduce{ (a, b) =>
counter.incrementAndGet()
a+b
}
println("after reduce: " + counter.get)
println("counted " + counter.get + " and result is " + reduced)
assert(20 == reduced)
assert(5 == counter.get)
}
The output is:
after filter: 1
after map: 2
after reduce: 5
counted 5 and result is 20
Question #2: How come the functions are being executed immediately?
I'm using Scala 2.10
You're probably thinking that
filter {
println
_ > 0
}
means
filter{ i =>
println
i > 0
}
but Scala has other ideas. The reason is that
{ println; _ > 0 }
is a statement that first prints something, and then returns the > 0 function. So it interprets what you're doing as a funny way to specify the function, equivalent to:
val p = { println; (i: Int) => i > 0 }
filter(p)
which in turn is equivalent to
println
val temp = (i: Int) => i > 0 // Temporary name, forget we did this!
val p = temp
filter(p)
which as you can imagine doesn't quite work out the way you want--you only print (or in your case do the increment) once at the beginning. Both your problems stem from this.
Make sure if you're using underscores to mean "fill in the parameter" that you only have a single expression! If you're using multiple statements, it's best to stick to explicitly named parameters.