The code below seems to work fine. But my question is that why the function is defined to take Strings as input but on the bottom, it has accepted some input integers?
case class
TempData(year:Int, month:String, Prec:Double, Maxtemp:Int, Meantemp:Int, Mintemp:Int)
def parseLine(line:String):TempData = {
val p = line.split(",")
TempData(p(1).toInt, p(3).toString, p(5).toDouble, p(6).toInt, p(8).toInt, p(10).toInt)
}
//> parseLine: (line: String)Tests.TempData
parseLine("Verizon, 2017, Alpha, October, gentlemen, 10.3, 5, Dallas, 67, schools, 42")
//> res0: Tests.TempData = TempData(2017, October, 10.3, 5, 67, 42)
The defined function parseLine(line: String) takes String type arguments, and you have passed it correctly. The confusion is that you are considering the numbers present in the string as integers(Int) which is incorrect. The numbers present in the string are of type String only because you have enclosed them in quotes. You can also reason out for using toInt for converting the the numbers from String to Int type.
Your function takes one single String as an argument..
def parseLine(line: String): TempData
And one single String is exactly what is passed to it:
scala> "Verizon,2017,Alpha,October,gentlemen,10.3,5,Dallas,67,schools,42"
res0: String = Verizon,2017,Alpha,October,gentlemen,10.3,5,Dallas,67,schools,42
The numbers are just plain text and are a part of that String, there are no integers involved. It's the body of your function that splits the String and extracts Ints from it.
edit: regarding the title of your post: all of that has nothing to do with your TempData case class. It seems that you are confusing the call of the case class constructor with the call to parseLine.
Related
I am taking a course on functional programming at the university and I am trying to solve a problem for an assignment. I have implemented a LinkedList in Scala and one of the functions I have implemented for that list is map if the following signature:
override def map(mapFunc: Int => Int): IntList
Now I have to write a function called encode which uses this map function in order to convert a String to a list of ASCII values of each character. For instance:
"abcdef" should return: SinglyLinkedIntList(97, 98, 99, 100, 101, 102)
My attemp so far is:
def encode(s: String): IntList = {
var a = "abcdef".map(_.toByte)
}
But this just converts the string to an Array. I am not sure how to continue from here. Could anyone help me understand how a problem like this could be solved using functional principles? Thanks in advance
Given a List of Any:
val l = List(2.9940714E7, 2.9931662E7, 2.993162E7, 2.9931625E7, 2.9930708E7, 2.9930708E7, 2.9931477E7)
I need to cast each item to Int.
Works:
l(1).asInstanceOf[Double].toInt
Not:
l.foreach{_.asInstanceOf[Double].toInt}
> java.lang.String cannot be cast to java.lang.Double
If
l.foreach{_.asInstanceOf[String].toDouble.toInt}
> java.lang.Double cannot be cast to java.lang.String
I'm new to Scala. Please tell me what I'm missing.
Why I can cast one item from list, but can't do this via iterator?
Thanks!
It seems as if a String somehow ended up in your List l.
Given a list that is structured like this (with mixed integers, Doubles, and Strings):
val l = List[Any](2.9940714E7, 2.9931625E7, "2.345E8", 345563)
You can convert it to list of integers as follows:
val lAsInts = l.map {
case i: Int => i
case d: Double => d.toInt
case s: String => s.toDouble.toInt
}
println(lAsInts)
This works for Doubles, Ints and Strings. If it still crashes with some exceptions during the cast, then you can add more cases. You can, of course, do the same in a foreach if you want, but in this case it wouldn't do anything, because you don't do anything in the body of foreach except casting. This has no useful side-effects (e.g. it prints nothing).
Another option would be:
lAsInts = l.map{_.toString.toDouble.toInt}
This is in a way even more forgiving to all kind of weird input, at least as long as all values are "numeric" in some sense.
However, this is definitely code-smell: how did you get a list with such a wild mix of values to begin with?
Your given List is of type Double. You can use simply map operation to convert it to Int type. try following code,
val l: List[Double] = List(2.9940714E7, 2.9931662E7, 2.993162E7, 2.9931625E7, 2.9930708E7, 2.9930708E7, 2.9931477E7)
//simply map the list and use toInt
val intLst: List[Int] = l.map(_.toInt)
print(intLst)
//output
//List(29940714, 29931662, 29931620, 29931625, 29930708, 29930708, 29931477)
But suppose you have same List as List[Any] instead then you can use following to convert it to Int.
val l: List[Any] = List(2.9940714E7, 2.9931662E7, 2.993162E7, 2.9931625E7, 2.9930708E7, 2.9930708E7, 2.9931477E7)
val intLst: List[Int] = l.map(_.asInstanceOf[Double].toInt)
It will give same output as above.
scala> val names = List("Peter", "Paul", "Mary")
names: List[String] = List(Peter, Paul, Mary)
scala> names.map(_.toUpperCase)
res12: List[String] = List(PETER, PAUL, MARY)
In this case, the underscore represents the only input argument, which is the element of names. This string is implicitly converted to StringOps and toUpperCase is invoked.
However, this does not work:
scala> names.map(StringOps.toUpperCase _)
<console>:14: error: value toUpperCase is not a member of object scala.collection.immutable.StringOps
names.map(StringOps.toUpperCase _)
I thought that this syntax is how I would get a reference to the function from the toUpperCase method.
First of all, there is no implicit conversion for _.toUppercase that convert String to StringOps, Since toUppercase is belong to String type, it's unnecessary convert to StringOps.
so for _.toUppercase is actual expand to high order function: val a: String => String = (str: String) => str.toUpperCase.
and StringOps implicit conversion defined in Predef.scala: augmentString, and this conversion only will occur when used StringOps's method, like: slice, stripSuffix, stripPrefix etc, for Example:
"name".slice(0, 2) // convert "name" to new StringOps("name").slice(0,2)
"name".stringPrefix
StringOps is a class, hence to use its method you'll need to instantiate an instance:
names.map( x => (new StringOps(x)).toUpperCase )
Or
names.map( new StringOps(_) toUpperCase )
StringOps is a wrapper class of the String class which means that it "enriches" this class with additional methods like the one you are using.
When you want to call a method on a string instance, you simply do:
instanceName.methodName
This is exactly what you are doing in your first example.
However, in your second example you are doing the following:
methodName(instanceName)
toUpperCase does not take an argument, it is called on the instance of the string itself.
I'm trying to read in a CSV and store it in a matrix-like array of arrays object. A roadblock I am hitting is that the strings are read in with the surrouding quotes - that is to say, the string "price" is not just the word price, but """"price"""" in scala. Consequently, I want to remove those surrounding quotes. I also want to make sure that any numeric values are coerced to Double/Int, as they are read in as strings.
What I have now:
val rawObs = io.Source.fromFile(file).getLines() .map(_.split(",")).toArray
// An example element of the array is:
//scala> rawObs(2)
//res93: Array[String] = Array("3", 0, "2013-02-27", 1, 52, 52, 1, "1", "kg")
// Here, I make a function to remove surrounding strings and return a string, or if there are
// not surrounding strings, return a Double.
def fixRawObs(x: String) = {
// if it was actually meant to be a string:
if(x.startsWith(""""""")){
// delete any " quotes
var y = x.replaceAll(""""""", "")
} else { // this means that x needs to be coerced to Int or Double
var y = x.toDouble
}
y // return y
}
// but this won't compile, it returns <console>:14: error: not found: value y
// If it did compile, I'd want to do something like this:
rawObs.map(_.map(fixRawObs(_)))
// (although, is there a better way?)
So, basically, my first question is how to fix my fixRawObs function, and secondarily, is this even an okay way to do this or is there some nicer way to accomplish what I want? What I'm doing feels kind of hackish.
I'm super super new to Scala so it would be greatly appreciated if answers didn't assume much knowledge. Thank you!
There are a few problems with your code:
You are trying to store Strings and Doubles in an Array. Since the closest common super type of Strings and Doubles is Any, you will have an Array[Any]. With an Array[Any], you will need to cast the values inside as Strings or Doubles whenever you want to use them, and this is not desirable.
Your function fixRawObs() is not compiling because it is trying to return an inaccessible variable. "y" is declared inside of curly braces, which makes it inaccessible outside of the curly braces. "y" actually is not even necessary, because an if statement in Scala returns a value, just like a function can. You could do this:
def fixRawObs(x: String) = {
if(x.startsWith(""""""")) x.replaceAll(""""""", "")
else x.toDouble
}
The return type of this function is "Any", though, so you would still have to cast the return values to the proper types manually. Again, this is not a good approach.
I recommend creating a class, so that you can have a custom data structure that references your values using their proper types.
case class Row(
col1: String, col2: Double, col3: String, col4: Double,
col5: Double, col6: Double, col7: Double, col8: String, col9: String
)
It would be best if you rename the values with appropriate, descriptive names.
You can then create your row objects like this:
def stripQuotes(s: String): String = {
if(s.startsWith("\"") && s.endsWith("\"")) s.dropRight(1).dropLeft(1)
else s
}
val csv = io.Source.fromFile(file)
val rows = (for {
line <- file.getLines
s = line.split(",")
if(s.size == 9)
} yield {
new Row(
stripQuotes(s(0)),
s(1).toDouble,
stripQuotes(s(2)),
s(3).toDouble,
s(4).toDouble,
s(5).toDouble,
s(6).toDouble,
stripQuotes(s(7)),
stripQuotes(s(8))
)
}).toArray
csv.close()
You probably want to use a library that parses CSV files instead of trying to get through the edge cases by yourself. There are many options for Scala/Java (one two).
If you're practicing Scala, I'll explain why it won't compile. The issue is that you're trying to return y, which is defined in the scope of your loop and isn't available outside of it.
In Scala, the last statement of the function is the return value. So making your if statement the last one in the function, and returning the replaced/parsed value right away will do what you want.
def fixRawObs(x: String) = {
x.startsWith("\"") match {
case true =>
x.replaceAll("\"", "")
case false =>
x.toDouble
}
}
Note that the function will return an instance of Any - the superclass of all classes in Scala. This is because you are returning a String in one clause and a Double in another.
Knowing the specific format of your data (e.g. is a given field always a double or is it always a string), you can rewrite it to be more precise and support the actual types.
This simple library: product-collections does something similar to what you are trying to do with your array of arrays. It also has an intuitive csv reader.
Update: the library now handles case classes directly:
val csv = Array("3", 0, "2013-02-27", 1, 52, 52, 1, "1", "kg").mkString(",")
csv: String = 3,0,2013-02-27,1,52,52,1,1,kg
scala> case class Row(
| col1: String, col2: Double, col3: String, col4: Double,
| col5: Double, col6: Double, col7: Double, col8: String, col9: String
| )
defined class Row
scala> CsvParser(Row).parse(new java.io.StringReader(csv))
res33: Seq[Row] = List(Row(3,0.0,2013-02-27,1.0,52.0,52.0,1.0,1,kg))
Scala has direct support for using hex and octal numbers:
scala> 01267 + 0100
res1: Int = 759
scala> 0x12AF + 0x100
res2: Int = 5039
but how do you do express an integer as a binary number in Scala ?.
If performance is not an issue, you can use a String and convert it to an integer.
val x = Integer.parseInt("01010101", 2)
Binary numbers aren't supported directly in part because you can easily convert from hexadecimal to binary and vice versa. To make your code clearer, you can put the binary number in a comment.
val x = 0x55 //01010101
In 2.10 you can create a string interpolator for that, e.g. it's possible to write b"0010" to mean 2. Using macros you can get rid of associated runtime overhead and do the conversion at compile-time. Take a look at Jason Zaugg's macrocosm to see it in action:
scala> b"101010"
res4: Int = 42
scala> b"102"
<console>:11: error: exception during macro expansion: invalid binary literal
b"102"
^
Using the new "implicit class" and "value class" mechanisms in 2.10, you can write something like this to add convenience methods without the overhead of object creation:
implicit class IntToBase( val digits:String ) extends AnyVal {
def base(b:Int) = Integer.parseInt( digits, b )
def b = base(2)
def o = base(8)
def x = base(16)
}
That allows you to do things like
"555".o // 365 decimal
and no IntToBase object is ever actually created.
You would need to be careful if you're converting from an integer that "looks like" binary as #agilesteel suggests. For example 0101.b would try to convert 65 decimal to binary (initial 0 signifying octal), whereas 101.b would try to convert 101 decimal to binary. It only really makes sense to try to convert from a String, for which there is Integer.parseInt, and from a number to the binary String representation, for which there is Integer.toString(x, 2).
I can't think of too many use-cases for programmatic binary literals. That said, they've made it to Java 7 as a number with prefix 0b, so I'd be surprised if they didn't appear in Scala soon. Java seems to have done fine without them for 15 years though.
If you are planning on using it a lot you can simulate the behavior with an implicit conversion.
object Extensions {
implicit def conversion(x: Int) = new BinaryInt(x)
class BinaryInt(x: Int) {
def b = {
// Conversion code like Integer.parseInt
// as Kim suggested
}
}
}
Now you can do stuff like
import Extensions._
val x = 0101.b
// or
val x = 5.b
You have to decide for yourself, which direction the conversion should go.
If you want to get a string of the binary representation of an Int you can call 4.toBinaryString. Padding is more difficult. You'll have to do something like: 4.toBinaryString.reverse.padTo(8, "0").reverse.mkString
def _0b(row: Int): Int = {
row.toString
.reverse
.split("")
.zipWithIndex
.map(x => (x._1.toInt, x._2))
.filter(_._1 == 1)
.map(x => Math.pow(2,x._2).toInt)
.sum
}
_0b(10011001) = 153
Though it is limited to 32Bit Values