how to map many strings to integer values in Scala? - scala

I have the following use case. User is providing two kinds of strings type and growth I need to convert the value of each into an integer from a known map and return the sum of both value.
What I did to map the strings is:
object Types extends Enumeration {
val T1 = 5
val T2 = 6
// list of values is really long!
val TYPE1 = "type1"
val TYPE2 = "type2"
}
object Growth extends Enumeration {
val G1 = 1
val G2 = 10
// list of values is really long!
val GROWTH1 = "growth1"
val GROWTH2 = "growth2"
}
Now consider user is calling sum("type1", "growth2") the expected output is 15 because 5+10=15
I wrote this skeleton function but I'm having trouble utilizing the enumeration objects. How can I do this without having so many if/else?
def sum(type: String, growth: String) : Int = {
var sum:Int = 0
????
return sum
}
if my approach is flawed I'm happy to try another

scala.Enumeration doesn't let you use syntax like this
object MyEnum extends Enumeration {
val value = "string"
}
It actually requires you to do something more like
object MyEnum extends Enumeration {
val value = Value("string")
}
and then you are passing not String but MyEnum.Value.
Since you are giving up on passing around Strings (at least in Scala 2, in Scala 3 you have an option to use type MyEnum = "value1" | "value2" and/or opaque type MyEnum), you create a sealed hierarchy instead
sealed abstract class Type(
val name: String,
val number: Int
) extends Product with Serializable
object Type {
case object Type1 extends Type("type1", 5)
case object Type2 extends Type("type2", 6)
}
sealed abstract class Growth(
val name: String,
val number: Int
) extends Product with Serializable
object Growth {
case object Growth1 extends Growth("growth1", 1)
case object Growth2 extends Growth("growth2", 10)
}
def sum(`type`: Type, growth: Growth) : Int =
`type`.number + growth.number
If you needed to construct a collection of all possible values, instead of scala.Enumeration you can use Enumeratum library instead
import enumeratum.values._
// IntEnumEntry and IntEnum specialize for Int
// and it allows to eliminate boxing
// The Int has to be defined as `val value: Int`
// findValues has to called so that the compiler
// can find all instances in the compile-time and
// populate the `values` collection
// Enumeratum then generate a lot of useful methods out of
// this list e.g. `withName` to find enum by its name
sealed abstract class Type(
val value: Int
) extends IntEnumEntry
object Type extends IntEnum[Type] {
case object Type1 extends Type(5)
case object Type2 extends Type(6)
val values = findValues
}
sealed abstract class Growth(
val value: Int
) extends IntEnumEntry
object Growth extends IntEnum[Growth] {
case object Growth1 extends Growth(1)
case object Growth2 extends Growth(10)
val values = findValues
}
def sum(`type`: Type, growth: Growth) : Int =
`type`.value + growth.value

This can be done by create a Map that gives the score for each String:
val scoreMap = Map(
"type1" -> 5,
"type2" -> 6,
"growth1" -> 1,
"growth2" -> 10,
)
The score can then be computed using foldLeft:
def score(strings: List[String]) =
strings.foldLeft(0){ case (sum, str) => sum + scoreMap.getOrElse(str, 0) }
score(List("type1", "growth2")) // 15
Values that are missing from the Map have the default value 0.

You can extend the class Val of Enumeration class.
Example:
object Types extends Enumeration {
final case class Pairing (label: String, value: Int) extends Val (value, label)
val TYPE1 = Pairing ("type1", 5)
val TYPE2 = Pairing ("type2", 6)
}
object Growths extends Enumeration {
final case class Pairing (label: String, value: Int) extends Val (value, label)
val GROWTH1 = Pairing ("growth1", 1)
val GROWTH2 = Pairing ("growth2", 10)
}
type Type = Types.Pairing
type Growth = Growths.Pairing
def sum(t: Type, g: Growth) = t.id + g.id
// usage:
import Types._
import Growths._
println(sum(TYPE1, GROWTH2)) // => 15
If you wish to use the operator + and reuse the val names :
object Types extends Enumeration {
final case class Entry (value: Int) extends Val (value) {
def +(g: Growth) = id + g.id
}
val type1 = Entry (5)
val type2 = Entry (6)
}
object Growths extends Enumeration {
final case class Entry (value: Int) extends Val (value) {
def +(t: Type) = t.id + id
}
val growth1 = Entry (1)
val growth2 = Entry (10)
}
type Type = Types.Entry
type Growth = Growths.Entry
// usage:
import Types._
import Growths._
println(type1 + growth2) // => 15
println(growth1 + type2) // => 7
println(growth1) // => "growth1"

Related

Serialize objects as JSON primitive type in Scala (Play Framework)

check out the following example:
object MyTrait {
def apply(value: Int): MyTrait = value match {
case 0 => A
case 1 => B
case 2 => C
case _ => C
}
def unapply(arg: MyTrait): Int = arg.value
}
case object A extends MyTrait {
val value = 0
}
case object B extends MyTrait {
val value = 1
}
case object C extends MyTrait {
val value = 2
}
case class Test(value: MyTrait)
implicit val testFormatter = Json.format[Test]
all right, this code won't work because i didn't create a JSON serializer for my type "MyTrait". I could do it the way the Play Framework show us to do in the documentation, but it would make a json looking like
{ value: { whatever: 1 } }
and i would like it to look like
{ value: 1 }
In short, i would like my MyTrait objetcs to be interpreted as primitive types (Int) instead of a nested Json Oject.
If someone could help me with that i would be greatful.
Thank you by advance!
The documentation indicates how to provide custom Writes, there to MyTrait to serialize only it's inner value.
sealed trait MyTrait {
def value: Int
}
case object A extends MyTrait {
val value = 0
}
case object B extends MyTrait {
val value = 1
}
case object C extends MyTrait {
val value = 2
}
object MyTrait {
import play.api.libs.json._
def apply(value: Int): MyTrait = value match {
case 0 => A
case 1 => B
case 2 => C
case _ => C
}
def unapply(arg: MyTrait): Int = arg.value
// ---v
implicit val writes: Writes[MyTrait] =
Writes[MyTrait] { v => JsNumber(v.value) }
}
Then when serializing MyTrait instances (note the type ascription bellow is required as Writes is invariant):
Json.toJson(A: MyTrait)
// --> res2: play.api.libs.json.JsValue = 0
And so about Test class:
case class Test(value: MyTrait)
object Test {
import play.api.libs.json._
implicit val writes: OWrites[Test] = Json.writes[Test]
}
Json.toJson(Test(A))
// ---> res1: play.api.libs.json.JsValue = {"value":0}
I would rather recommend to have a look at Enumeratum for the enumerated type MyTrait, or to refactor MyTrait as a Value class and use Json.valueFormat as below.
import play.api.libs.json._
final class MyTrait private(val value: Int) extends AnyVal
object MyTrait {
val A = new MyTrait(0)
val B = new MyTrait(1)
val C = new MyTrait(2)
implicit val format: Format[MyTrait] = Json.valueFormat[MyTrait]
}
case class Test(value: MyTrait)
object Test {
implicit val format: OFormat[Test] = Json.format
}
scala> Json.toJson(Test(MyTrait.A))
res0: play.api.libs.json.JsValue = {"value":0}

Covariant data structure with numeric types

How do I create a covariant data structure containing numeric data types?
In scala I can create a covariant data structure, with a hierarchy of type parameters:
abstract class Car[+T] {
def value: T
}
class RaceCar extends Car[RacingWheel] {
def value: RacingWheel = new RacingWheel
}
class NormalCar extends Car[BoringWheel] {
def value: BoringWheel = new BoringWheel
}
class Wheel
case class RacingWheel() extends Wheel
case class BoringWheel() extends Wheel
object Car {
def main(args: Array[String]): Unit = {
val rCar: Car[Wheel] = new RaceCar
val nCar: Car[Wheel] = new NormalCar
val listCars: List[Car[Wheel]] = List(rCar, nCar)
}
}
However, when I replace the Wheel's with numeric data types I run into a problem, because the numeric data types do not have a common type parent other than AnyVal:
abstract class Item[+N] {
def value: N
}
class IntItem(x : Int) extends Item[Int] {
override def value: Int = x
}
class DoubleItem(x : Double) extends Item[Double] {
override def value: Double = x
}
object Item {
def main(args: Array[String]): Unit = {
// ERROR Expression of IntItem does not conform to expected type Item[Number]
val iItem: Item[Number] = new IntItem(10)
// ERROR Expression of DoubleItem does not conform to expected type Item[Number]
val dItem: Item[Number] = new DoubleItem(10.9)
val l: List[Item[Number]] = List(iItem, dItem)
// ERROR: Expression of IntItem does not conform to expected type Item[Double]
val iItem2: Item[Double] = new IntItem(10)
val dItem2: Item[Double] = new DoubleItem(10.9)
val l2: List[Item[Double]] = List(iItem2, dItem2)
}
}
Outside of the covariant data structure I get mixed results:
object NumberMain {
val a : Int = 5
val b : Double = 10.0
val list : List[Number] = List(a, b)
// works, so Number must be related to Int and Double, right?
val x : Number = 5
// ERROR: Expression of type Number doesn't conform to expected type Int
val y : Int = x
// does not work, so Number is not related to Int and Double after all...
}
What changes do I have to make to the Item data structure? Can I tell it that all items can be seen as of type numeric?
In Scala, Int and Double do not share hierarchy with a common Number class. The Typeclass pattern can help you in these cases to define extra features for types without a common supertype.
trait MyNumber[A]
implicit case object IntMyNumber extends MyNumber[Int]
implicit case object DoubleMyNumber extends MyNumber[Double]
class Item[N](x: N)(implicit n: MyNumber[N]) {
def value: N = x
}
The Item class constructor now expects a second group of parameters labeled with the implicit keyword. If you call the method without this group of parameters, the compiler tries to find it (an instance of MyNumber) from variables or imports also labeled with implicit (Where does Scala look for implicits?).
We're just defining here that Int and Double implements MyNumber, but it doesn't do anything. We can write then the class as:
class Item[N : MyNumber](x: N) {
def value: N = x
}
And it works as we expected:
val iItem = new Item(1)
val dItem = new Item(1.0)
scala> iItem.value
res1: Int = 1
scala> dItem.value
res2: Double = 1.0
If we try to build an item of anything without an instance of MyNumber, the compiler throws an error:
val sItem = new Item("S")
error: could not find implicit value for evidence parameter of type MyNumber[String]
If you want MyNumber to be any numeric, Scala implements a Numeric typeclass.
Edit
If you want to add some features to the typeclass that are different for each number, you can do:
trait MyNumber[A] {
def doStuff(a: A): Unit
}
implicit case object IntMyNumber extends MyNumber[Int] {
def doStuff(number: Int): Unit = println("Int")
}
implicit case object DoubleMyNumber extends MyNumber[Double] {
def doStuff(number: Double): Unit = println("Double")
}
case class Item[+N](x: N)(implicit myNumber: MyNumber[N]) {
def value: N = x
def doStuff = myNumber.doStuff(x)
}

how to get enums value using case classes in scala

I am trying to implement enum using case class in scala.
with reference to question type parameter in traits to define data types in scala
sealed trait ElementType[+A] {
def length: Int
}
sealed trait Tag extends ElementType[Int] {
override def length: Int = 0
}
case object Tag100 extends Tag
sealed trait Value[+A] extends ElementType[A] {
override def length: Int = 0
}
final case class Value100(a: String) extends Value[String] {
override def length: Int = a.length
}
case class MessageId(tag: Tag, value: Value[String]){
}
case class MessageRepType(tag: Tag[Int], value:MessageType ){
}
sealed trait Message[T] {def typeCode: T;}
object MessageTypes {
sealed trait MessageType[Int] extends Message[Int]
case object TYPE1 extends MessageType[Int]{val typeCode = 0;}
case object TYPE2 extends MessageType[Int]{val typeCode = 1;}
}
case class ABCMessage (
id:MessageId,
messageType:MessageRepType)
I want to print a message with something like below id and type in new line
type value
100 abc
200 0
Explanation: 100 abc represents MessageId
200 0 represnets MessageRepType and O represent TYPE1
Assuming your "message" is a ABCMessage(MessageId(Tag100, Value100("abc"),MessageRepType(Tag200, TYPE1)) (also assuming Tag200 is similar to Tag100 and case class MessageRepType(tag: Tag[Int], value:MessageType) is case class MessageRepType(tag: Tag, value:MessageType) and sealed trait MessageType extends Message[Int]):
val sep = System.lineSeparator
def convertTag(tag: Tag): Int = tag match {
case Tag100 => 100
case Tag200 => 200
}
def convertMessageId(message: MessageId): String = {
val typ = convertTag(message.tag)
val value = message.value match {
case Value100(v) => v
}
s"$typ\t$value"
}
val id = convertMessageId(message.id)
def convertType(message: MessgeRepType): String = {
val typ = convertTag(message.tag)
val value = message.value match {// I would put typeCode to MessageType, but it is not there, so pattern matching
case TYPE1 => TYPE1.typeCode
case TYPE2 => TYPE2.typeCode
}
s"$typ\t$value"
}
val typ = convertType(message)
val message: ABCMessage = ABCMessage(MessageId(Tag100, Value100("abc"),MessageRepType(Tag200, TYPE1))
val messageAsString = Seq("type\tvalue", id, typ).mkString(sep)
println(messageAsString)
A different approach as still not sure what you want to achieve. The other answer with pattern matching is what I would prefer, more idiomatic.
You can use something like a simplified the visitor pattern (removed the irrelevant parts):
trait Show {
def show: String
}
sealed trait Tag extends Show {
override def show: String = this.getClass.getSimpleName.init
}
//object Tag {
case object Tag100 extends Tag
case object Tag200 extends Tag
//}
This way you can show the tags like this:
Tag100.show // Tag100
The case classes can also implement show:
sealed trait Value[+A] extends Show {
def v: String
override def show: String = v
}
case class Value100(v: String) extends Value[String]
You can use this in other methods too to implement the printing you want:
def convertMessageId(message: MessageId): String = {
val typ = message.tag.show
val value = message.value.show
s"$typ\t$value"
}
val id = convertMessageId(message.id)
def convertType(message: MessgeRepType): String = {
val typ = message.tag.show
val value = message.value.show
s"$typ\t$value"
}
val message: ABCMessage = ABCMessage(MessageId(Tag100, Value100("abc"),MessageRepType(Tag200, TYPE1))
val messageAsString = Seq("type\tvalue", id, typ).mkString(sep)
println(messageAsString)
If type safety and easy, generic usage is also important, take a look at Shapeless' offerings and this blog post.
If type safety is not so important, maybe for your use case it is enough to know that case classes implement Product, so you can visit them one-by-one and use its content.
Alternative solution might be to convert your datastructure to for example JSON (there are many Scala libs for that) and use that for further processing. Still generic, but not type-safe.
(This question might also be relevant.)

DEfine scala enum with 2 properties

I have several fields that i want to create enum from but those fields have 2 values: Valid and Not valid:
object MyEnum extends Enumeration
{
val
type1, // this type is valid
type2, // valid
type3, // valid
type4, // Not valid
type5, // Not valid
type6, // Not valid
type7 // Not valid
= value
}
Any suggestion how to distinguish between Valid and Not valid via code ? or maybe it must separate into 2 enums ?
You can create a function on the Enum object and use the .toString part of Enumeration.Value
object MyEnum extends Enumeration {
val type1 = Value("valid")
val type2 = Value("valid")
val type3 = Value("valid")
val type4 = Value("Not valid")
val type5 = Value("Not valid")
val type6 = Value("Not valid")
val type7 = Value("Not valid")
lazy val lookup = this.values.map { n => (n, n.toString == "valid") }.toMap
def isValid(myEnum: MyEnum.Value) = lookup.getOrElse(myEnum, false)
}
MyEnum.isValid(MyEnum.type1) //> res0: Boolean = true
MyEnum.isValid(MyEnum.type7) //> res1: Boolean = false
Sources:
https://github.com/scala/scala/blob/v2.11.7/src/library/scala/Enumeration.scala#L146
http://www.scala-lang.org/api/current/#scala.Enumeration$Value
There's a different pattern you could use that does not extend Enumeration but acts similarly:
object MyEnum extends Serializable {
abstract sealed class MyEnum(
val ordinal: Int, val isValid: Boolean
) extends Ordered[MyEnum] with Serializable {
def compare(that: MyEnum) = that.ordinal compare this.ordinal
def toInt: Int = this.ordinal
}
case object Type1 extends MyEnum(0, true)
case object Type2 extends MyEnum(1, true)
case object Type3 extends MyEnum(2, true)
case object Type4 extends MyEnum(3, false)
case object Type5 extends MyEnum(4, false)
case object Type6 extends MyEnum(5, false)
case object Type7 extends MyEnum(6, false)
def fromInt(i: Int): MyEnum = values.find(_.ordinal == i).get
val values = Set(
Type1, Type2, Type3, Type4, Type5, Type6, Type7
)
}
This way you are able to assign in a way like val myEnum: MyEnum.MyEnum = MyEnum.Type1 similar to your example above and you will have access to myEnum.isValid. Similarly you can access it ordinally with the fromInt method. Such as MyEnum.fromInt(0).isValid. If you provide some more context on what you're trying to do with the values I may be able to provide a better solution. Hope this helps.

How do I create an enum in scala that has an extra field

In Java I have something like this
public enum FlatFileHeaderMapping {
HEADER_EL(1),
HEADER_RESERVED1(5),
HEADER_RESERVED2(2),
HEADER_MESSAGE_TYPE(4)
public final int fieldSize;
private FlatFileHeaderMapping(int fieldSize) {
this.fieldSize = fieldSize;
}
}
which I can then use it place each line into a map and later access the keys in the map via this enum (like symbols)
Enumeration does not have this quality as far as I can see, and case classes are not ordered like the enum declarations - so cannot be used to match a record layout as shown above. At least not without the support of an ordered collection.
I could be missing something obvious, hence the question!
Thanks
Ray
overthink is right, but there's a less verbose way of declaring the case objects:
sealed abstract class FlatFileHeaderMapping(val fieldSize: Int)
case object HEADER_EL extends FlatFileHeaderMapping(1)
case object HEADER_RESERVED1 extends FlatFileHeaderMapping(5)
case object HEADER_RESERVED2 extends FlatFileHeaderMapping(2)
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping(4)
You could try using case objects:
sealed trait FlatFileHeaderMapping { val fieldSize: Int }
case object HEADER_EL extends FlatFileHeaderMapping { val fieldSize = 1 }
case object HEADER_RESERVED1 extends FlatFileHeaderMapping { val fieldSize = 5 }
case object HEADER_RESERVED2 extends FlatFileHeaderMapping { val fieldSize = 2 }
case object HEADER_MESSAGE_TYPE extends FlatFileHeaderMapping { val fieldSize = 4 }
You can then use the enum like so:
object Test {
def foo(x: FlatFileHeaderMapping) {
val result =
x match {
case HEADER_EL => "it's a HEADER_EL!"
case other => "its field size is: " + other.fieldSize
}
println(result)
}
def main(args: Array[String]) {
foo(HEADER_EL)
foo(HEADER_MESSAGE_TYPE)
}
}
The main nicety you get here is compile-time checking that all enum values are handled. i.e in the x match { ... } code above you'd get a compile error if you didn't have the 'case other => ...` clause in there.
I'm pretty much just restating this answer, which lists pros and cons of this approach.
object Direction extends Enumeration {
val North = Value("North")
val East = Value("East")
val South = Value("South")
val West = Value("West")
}
scala> import Direction._
scala> values foreach println
scala> val map = HashMap(North -> 1, South -> 2)
This is answered in Enumeration with constructor and lookup table
A simpler solution exist for integer value:
object FlatFileHeaderMapping extends Enumeration {
type FlatFileHeaderMapping = Value
val HEADER_EL = Value(1, "HEADER_EL")
val HEADER_RESERVED1 = Value(5, "HEADER_RESERVED1")
val HEADER_RESERVED2 = Value(2, "HEADER_RESERVED2")
val HEADER_MESSAGE_TYPE = Value(4, "HEADER_MESSAGE_TYPE")
}
Reproducing the contents of the accepted answer, as it's hidden behind a broken Tumblr link (that I accessed via Archive.org), which in turn points to this page.
trait Enum { //DIY enum type
import java.util.concurrent.atomic.AtomicReference //Concurrency paranoia
type EnumVal <: Value //This is a type that needs to be found in the implementing class
private val _values = new AtomicReference(Vector[EnumVal]()) //Stores our enum values
//Adds an EnumVal to our storage, uses CCAS to make sure it's thread safe, returns the ordinal
private final def addEnumVal(newVal: EnumVal): Int = { import _values.{get, compareAndSet => CAS}
val oldVec = get
val newVec = oldVec :+ newVal
if((get eq oldVec) && CAS(oldVec, newVec)) newVec.indexWhere(_ eq newVal) else addEnumVal(newVal)
}
def values: Vector[EnumVal] = _values.get //Here you can get all the enums that exist for this type
//This is the trait that we need to extend our EnumVal type with, it does the book-keeping for us
protected trait Value { self: EnumVal => //Enforce that no one mixes in Value in a non-EnumVal type
final val ordinal = addEnumVal(this) //Adds the EnumVal and returns the ordinal
def name: String //All enum values should have a name
override def toString = name //And that name is used for the toString operation
override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
override def hashCode = 31 * (this.getClass.## + name.## + ordinal)
}
}
//And here's how to use it, if you want compiler exhaustiveness checking
object Foos extends Enum {
sealed trait EnumVal extends Value /*{ you can define your own methods etc here }*/
val F = new EnumVal { val name = "F" }
val X = new EnumVal { val name = "X" }
}
/**
scala> Foos.values.find(_.name == "F")
res3: Option[Foos.EnumVal] = Some(F)
scala> Foos.X.ordinal
res4: Int = 1
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
}
<console>:10: warning: match is not exhaustive!
missing combination $anon$1
missing combination $anon$2
scala> def doSmth(foo: Foos.EnumVal) = foo match {
case Foos.X => println("pigdog")
case Foos.F => println("dogpig")
}
doSmth: (foo: Foos.EnumVal)Unit
**/
//But if you don't care about getting exhaustiveness warnings, you can do:
object Foos extends Enum {
case class EnumVal private[Foos](name: String) extends Value /* { you can define your own methods and stuff here } */
val F = EnumVal("F")
val X = EnumVal("X")
}
/**
Which is a bit less boilerplatey.
Cheers,
√
**/