Related
Right now I have a def that returns a Tuple2[String, Int], or (String, Int).
def makeTuple(name: String, age: Int) : (String, Int) = {
(name, age)
}
val nameAge = makeTuple("test", 12)
println(nameAge._1) // prints test
println(nameAge._2) // prints 12
Ok this works, but Is there a way to give this tuple names?
For example:
println(nameAge.name)
println(nameage.age)
As jubobs mentioned we can do it like this:
Using the case class:
case class NameAge(name:(String), age:(Int))
def makeTuple(name: String, age:Int): NameAge = {
NameAge(name, age)
}
val nameAge = makeTuple("test",12)
println(nameAge.name, nameAge.age)
Using val:
def makeTuple1(name: String, age:Int): (String, Int) = {
(name, age)
}
val (name, age) = makeTuple1("test", 12)
println(name, age)
Note that case classes can be defined locally, so they don't pollute the namespace and can be close to where you use them:
def f() = {
...
case class NameAge(name: String, age: Int)
val nameAge = NameAge("test", 12)
println(nameAge.name)
println(nameAge.age)
...
}
Alternately, you could use a library like Scala Records:
val nameAge = Rec("name" -> "test", "age" -> 12)
println(nameAge.name)
println(nameAge.age)
You can just assign both names to the output of your function:
val (name, age) = makeTuple("test", 12)
Or even get rid of the function
val (name, age) = ("test", 12)
Then (in both cases) you can perform your prints as follows:
println(name)
println(age)
With reference to the link below
Scala: curried constructors
class Person(var name : String, var age : Int, var email : String)
def mkPerson = (n : String) => (a : Int) => (e : String) => new Person(n,a,e)
def makeName(s:String):String="Hello"+s
def makeAge(age:Int):Int=age
def makeEmail(email:String):String=email
val s=mkPerson(makeName("abcd"))
val t1=s(makeAge(2))
val t2=t1(makeEmail("abc#gmail.com"))
I have created methods makeName,makeAge,makeEmail to enrich the values. The actual use case is different
Question
Is there any possible ways to achieve the above through case classes
I don't want to use variables s,t1,t2
Can we achieve the above by Partially applied functions
Not sure curried functions are necessary / helpful in this case, if you're OK with a mkPerson that takes 3 arguments:
case class Person private(name: String, age: Int, email: String)
def makeName(s: String): String = "Hello" + s
def makeAge(age: Int): Int = age
def makeEmail(email: String): String = email
def mkPerson(name: String, age: Int, email: String): Person =
Person(makeName(name), makeAge(age), makeEmail(email))
println(Person("Jane", 29, "jane#mail.com")) // no enrichment: Person(Jane,29,jane#mail.com)
println(mkPerson("Jane", 29, "jane#mail.com")) // enriched: Person(HelloJane,29,jane#mail.com)
Then, if you really need it, you can curry mkPerson:
val curried = (mkPerson _).curried
curried("Jane")(29)("jane#mail.com") // same enriched result
How should I extract the value of a field of a case class from a given String value representing the field.
For example:
case class Person(name: String, age: Int)
val a = Person("test",10)
Now here given a string name or age i want to extract the value from variable a. How do i do this? I know this can be done using reflection but I am not exactly sure how?
What you're looking for can be achieve using Shapeless lenses. This will also put the constraint that a field actually exists on a case class at compile time rather than run time:
import shapeless._
case class Person(name: String, age: Int)
val nameLens = lens[Person] >> 'name
val p = Person("myName", 25)
nameLens.get(p)
Yields:
res0: String = myName
If you try to extract a non existing field, you get a compile time error, which is a much stronger guarantee:
import shapeless._
case class Person(name: String, age: Int)
val nonExistingLens = lens[Person] >> 'bla
val p = Person("myName", 25)
nonExistingLens.get(p)
Compiler yells:
Error:(5, 44) could not find implicit value for parameter mkLens: shapeless.MkFieldLens[Person,Symbol with shapeless.tag.Tagged[String("bla")]]
val nonExistingLens = lens[Person] >> 'bla
don't know exactly what you had in mind, but a match statement would do, it is not very generic or extensible with regards changes to the Person case class, but it does meet your basic requirements of not using reflection:
scala> val a = Person("test",10)
a: Person = Person(test,10)
scala> def extract(p: Person, fieldName: String) = {
| fieldName match {
| case "name" => p.name
| case "age" => p.age
| }
| }
extract: (p: Person, fieldName: String)Any
scala> extract(a, "name")
res1: Any = test
scala> extract(a, "age")
res2: Any = 10
scala> extract(a, "name####")
scala.MatchError: name#### (of class java.lang.String)
at .extract(<console>:14)
... 32 elided
UPDATE as per comment:
scala> case class Person(name: String, age: Int)
defined class Person
scala> val a = Person("test",10)
a: Person = Person(test,10)
scala> def extract(p: Person, fieldName: String) = {
| fieldName match {
| case "name" => Some(p.name)
| case "age" => Some(p.age)
| case _ => None
| }
| }
extract: (p: Person, fieldName: String)Option[Any]
scala> extract(a, "name")
res4: Option[Any] = Some(test)
scala> extract(a, "age")
res5: Option[Any] = Some(10)
scala> extract(a, "name####")
res6: Option[Any] = None
scala>
I think it can do by convert case class to Map, then get field by name
def ccToMap(cc: AnyRef) =
(Map[String, Any]() /: cc.getClass.getDeclaredFields) {
(a, f) =>
f.setAccessible(true)
a + (f.getName -> f.get(cc))
}
Usage
case class Person(name: String, age: Int)
val column = Person("me", 16)
println(ccToMap(column))
val name = ccToMap(column)["name"]
I have created a class Path which should be uniquely identifiable by occupation and last.
case class Path(occupation: BitSet, last: Int) {
var cost = 0
def setCost(cost: Int) {
this.cost = cost
}
def getCost(): Int = {
return cost
}
}
}
Also, I would like it to be sortable by count, which I made a field.
implicit val pathOrd = Ordering.by((p: Path) => p.getCost)
The problem is that when I am sorting it(as you can see in the line above), I get java.lang.NullPointerException on that line.
Why is this happening?
Can I store my data better?
Your code gives no exceptions to me using this code:
# class Path(occupation: BitSet, last: Int) {
var cost = 0
def setCost(cost: Int) {
this.cost = cost
}
def getCost(): Int = {
return cost
}
}
defined class Path
# List(new Path(BitSet(), 3))
res6: List[Path] = List(cmd5$Path#3ef8de39)
# implicit val pathOrd = Ordering.by((p: Path) => p.getCost)
pathOrd: Ordering[Path] = scala.math.Ordering$$anon$9#5f478e42
# res6.sorted
res9: List[Path] = List(cmd5$Path#3ef8de39)
You are missing val for occupation and last
class Path(val occupation: BitSet, val last: Int)
I would suggest you to create a case class
case class Path(occupation: BitSet, last: Int)
It will have equals, and hashCode based on it's fields, toString, apply, unapply and copy methods.
I am not sure if you really need to modify the cost, if it is calculated based on other values you could make it a method
case class Path(occupation: BitSet, last: Int) {
def cost: Int = 42
}
if it's not then it should be a field. I want to encourage you to use immutable structures, which would mean doing:
case class Path(occupation: BitSet, last: Int, cost: Int)
Adding a field with setter and getter in scala is as simple as this:
class Path(val occupation: BitSet, val last: Int) {
var cost = 0
}
You can use it like this:
val path = new Path(BitSet(), 3)
path.cost = 12
println(path.cost)
Here is a simpler version of the code that uses Scala's conversions for using var to indicate mutable state in a class:
case class Path
(occupation: BitSet, // Immutable (will never change)
var cost: Int = 0, // Mutable
var last: Int = 0 // Mutable
)
// Implementing the Ordering TypeClass
implicit val pathOrd = Ordering.by((p: Path) => p.cost)
val t = new Path(BitSet(1,2), 0, 0)
t.cost = 2 // changing cost
val data = Seq(new Path(BitSet(1,2), 0, 0), new Path(BitSet(1), 3, 0), new Path(BitSet(1), 2, 0))
println(data.sorted)
Why can't I do the following:
def multiply(a: Int, b: Int = a) = a * b
or
case class Point(x: Int, y: Int = x)
Is there another way to achieve the same? The reason I need this is because sometimes, the arguments having different values is more the exception than the rule. Take for example:
case class User(name: String, age: String, description: String, displayName: String = name, online: Boolean = false)
In 90% of the cases, display name and name should be the same, but in a few edge cases, it should not. Having one parameter default to the value of another would be very useuful. Is there a way to do this? And if not, why?
Yes. Parameters in a parameter list can refer to argument values in previous parameter lists, similar to how type inference can refer to types in previous parameter lists:
def multiply(a: Int)(b: Int = a) = a * b
case class Point(x: Int)(y: Int = x)
case class User(name: String, age: String, description: String)(displayName: String = name, online: Boolean = false)
should work.
One way can be to define case class as
case class User(name: String, age: String, description: String, displayName: String, online: Boolean = false) {
def this(name: String, age: String, description: String, online: Boolean = true) =
this(name, age, description, name, online)
}
Then you can create case class as
val user = new User("User name", "5", "description")
//> user : User = User(User name,5,description,User name,true)
user.displayName
//> res0: String = User name
val userWithDisplayName = new User("User name", "5", "description", "displayName")
//> userWithDisplayName : User = User(User name,5,description,displayName,false)
You can also override apply method in the companion object. That way you will not have to write new before creating the object
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class User(name: String, age: String, description: String, displayName: String, online: Boolean = false)
object User {
def apply(name: String, age: String, description: String, online: Boolean) =
new User(name, age, description, name, online)
}
// Exiting paste mode, now interpreting.
defined class User
defined module User
scala> User("User name", "5", "description", "displayName")
res0: User = User(User name,5,description,displayName,false)
scala> User("User name", "5", "description", true)
res1: User = User(User name,5,description,User name,true)