I have an array of String which I will be receiving from an arbitrary function. I want to use the elements of the array to create a new class at runtime (not a new object of an existing class). Let me give you an example
val keyCounts = Array[String]
def newDomainPartioner(keyCounts : Array[Strings]) : DomainPartitioner{
return class DomainPartitioner with Serializable {
def getPartition(key: Any): Int = key match {
case <first element of keyCount> =>
1
case <second element of keyCount> =>
1
case <third element of keyCount> =>
1
case <forth element of keyCount> =>
1
case _ => 0
}
}
}
Is there a way to achieve the intended functionality ?
You can use reflection to generate a new class at runtime, see this question for more details:
Generating a class from string and instantiating it in Scala 2.10
However, it sounds like you would be better off having a single class that encompasses the behaviour you want, and returning an instance of that class, eg:
class DomainPartitioner(keyCounts: Array[String]) with Serializable {
def getPartition(key: Any): Int = keyCounts indexOf key match {
case 1 =>
1
case 2 =>
1
case 3 =>
1
case x if myConditionIsTrue(x) =>
1
case _ => 0
}
}
def newDomainPartioner(keyCounts : Array[Strings]) =
new DomainPartitioner(keyCounts)
val arrayStrings: Array[String] = Array("we","are","great")
def arrayCaseClass(schema:Array[String], className:String)
:String = {
def arrayElements(element:String):String = {
val types = element
element match {
case x if !x.isEmpty => s" $element:String"
case _ => s" $element:$types"
}
}
val fieldsName = schema.map(arrayElements).mkString(",\n ")
s"""
|case class $className (
| $fieldsName
|)
""".stripMargin
}
println(arrayCaseClass(arrayStrings, "ArrayCaseClass"))
Related
I've got various case classes with different fields inherit some trait. All are mixed in a List. What is the way to collect (or group by) specific field's values?
sealed trait Template
object Template {
case class TemplateA(field: String) extends Template
case class TemplateB extends Template
}
object Runner {
def main(args: String*) {
val list = List(TemplateA("abc"), TemplateB, Template("cde"))
// need to output something like "abc;1", "cde;1"
}
}
Totally agree with #LuisMiguel, just to show one way of doing this, here's what I can think of:
trait Template { val field: Option[String] }
case class TemplateA(field: Option[String]) extends Template
case class TemplateB() extends Template { override val field: Option[String] = None }
val list: List[Template] = List(
TemplateA(Some("abc")),
TemplateB(),
TemplateA(Some("cde"))
)
list.collect {
case template if template.field.nonEmpty =>
template.field.get
}.groupMapReduce(identity)(_ => 1)(_ + _)
// res8: Map[String, Int] = Map("abc" -> 1, "cde" -> 1)
Or if you want to get rid of the Optional argument when instantiating TemplateA instances, you can also do this:
case class TemplateA(private val value: String) extends Template {
override val field: Option[String] = Option(value)
}
val list: List[Template] = List(TemplateA("abc"), TemplateB(), TemplateA("cde"))
As #DmytroMitin mentioned, we can do a bit of refactoring to avoid using ifs in our collect function, I'd rather use some sort of unapply function, that can extract the field value of TemplateA instances:
object Template { // or any name as you wish
def unapply(t: Template): Option[String] = t match {
case TemplateA(Some(value)) => Option(value)
case _ => None
}
}
And then, we can use pattern matching:
list.collect {
case Template(field) => field
}.groupMapReduce(identity)(_ => 1)(_ + _)
I have an array of objects of type Either[A, B]. If I know for a particular element whether it is an A or a B, how do I call a method on it that only exists on one of the 2 types. For example:
import scala.util.Random
object EitherTest extends App {
def newObj(x: Int): Either[A,B] = {
if (x == 0)
Left(new A())
else
Right(new B())
}
val random = new Random()
val randomArray = (0 until 10).map(_ => random.nextInt(2))
val eitherArray = randomArray.map(newObj)
(0 until 10).foreach(x => randomArray(x) match {
case 0 => eitherArray(x).aMethod()
case 1 => eitherArray(x).bMethod()
case _ => println("Error!")
})
}
class A {
def aMethod() = println("A")
}
class B {
def bMethod() = println("B")
}
When I compile this code, the lines
case 0 => eitherArray(x).aMethod()
case 1 => eitherArray(x).bMethod()
both have the error "value aMethod is not a member of Either[A,B]". How can I solve this?
I don't know why fold doesn't get the respect it deserves. It can be so useful.
eitherArray.foreach(_.fold(_.aMethod(), _.bMethod()))
Well, you can do it if you exctract the logic to another method, and do some pattern matching over the value Either, then check if it is Right or Left, and that's it!
object HelloWorld {
import scala.util.Random
def main(args: Array[String]) {
val random = new Random()
val randomArray = (0 until 10).map(_ => random.nextInt(2))
val eitherArray = randomArray.map(EitherTest.newObj)
(0 until 10).foreach(x => randomArray(x) match {
case 0 => EitherTest.callmethod(eitherArray(x))
case 1 => EitherTest.callmethod(eitherArray(x))
case _ => println("Error!")
})
println("Hello, world!")
}
}
class EitherTest
object EitherTest {
def callmethod(ei : Either[A,B]) = {
ei match {
case Left(a) => a.aMethod()
case Right(b) => b.bMethod()
}
}
def newObj(x: Int): Either[A,B] = {
if (x == 0)
Left(new A())
else
Right(new B())
}
}
class A {
def aMethod() = println("A")
}
class B {
def bMethod() = println("B")
}
Will print for you, for one random example:
A
B
A
B
A
A
A
B
B
B
Hello, world!
Basically, the way you do with Either is projections: Either.left gives you the projection of the left type, and Either.right gives you that of the right.
The projections are somewhat similar to options, in that they can be empty (if your Either is a Right, then the left projection is empty and vice versa), and you can use the usual monadic transformations with them, like map, flatMap, foreach, getOrElse etc.
Your example, could look like this:
randomArray.foreach { either =>
either.left.foreach(_.aMethod)
either.right.foreach(_.bMethod)
}
You could also use pattern-matching instead, that's less general, but, perhaps looks a bit clearer in this case:
randomArray.foreach {
case Left(a) => a.aMethod
case Right(b) => b.bMethod
}
In order to get height of a binary tree I use this code :
object optionfun {
println("Welcome to the Scala worksheet")
case class Node(var isVisited: Boolean, var leftNode: Option[Node], var rightNode: Option[Node], name: String) {
def this(name: String) = this(false, None, None, name)
}
val a = new Node("a")
val b = new Node("b")
val c = new Node("c")
a.leftNode = Some(b)
a.rightNode = Some(c)
def getHeight(root: Option[Node]): Int = {
//if root contains a None type then it should return 0, should I pattern match on the option type here?
Math.max(getHeight(root.leftNode),
getHeight(root.rightNode.get)) + 1
}
getHeight(a)
}
But I receive compiler error for line :
Math.max(getHeight(root.leftNode)
error is :
Multiple markers at this line - value leftNode is not a member of Option[optionfun.Node] - value
leftNode is not a member of Option[Node]
I've mixed up the types somehow but I don't know what to pass into getHeight method "root.leftNode" is of type Option ?
The problem with accepting root as an Option[Node] is that you can't just call root.leftNode, you have to map or match the Option.
You could re-write it without the Option like this:
def getHeight(root: Node): Int = {
Math.max(
root.leftNode.map(getHeight(_)).getOrElse(0),
root.rightNode.map(getHeight(_)).getOrElse(0)
) + 1
}
Or with Option:
def getHeight(root: Option[Node]): Int = root match {
case Some(r) => Math.max(getHeight(r.leftNode), getHeight(r.rightNode)) + 1
case None => 0
}
I have some code
case class A(s:String) {val x = "hello"}
Why can't I access the static variable x without instantiating the class A? If I type
A.x
I get the following error:
error: value x is not a member of object A
Edit:
I missed out mentioning the remaining code. Here is the example that I would like to use:
abstract class A { val name:String }
case class B(i:Int) extends A { val name = "B" }
case class C(b:Boolean) extends A { val name = "C" }
def getType(s:String) = s match {
case B.name => println ("Object B")
case C.name => println ("Object C")
}
The error:
scala> def getType(s:String) = s match {
| case B.name => println ("Object B")
| case C.name => println ("Object C")
| }
<console>:11: error: value name is not a member of object B
case B.name => println ("Object B")
^
<console>:12: error: value name is not a member of object C
case C.name => println ("Object C")
^
As to why use case classes, the case classes are not defined for this purpose. Elsewhere I have some code like:
def func(a:A) = a match {
case b:B =>
case c:C =>
...
}
Well, you cannot call the "static" variable x, because in Scala there are no static variables. You are declaring x to be a regular member of class A, which you could access if you had an instance of class A.
What you try to do by calling A.x is accessing a value with the name "A". There happens to be such a value in scope - the compiler generated companion object for your case class A.
But this object A has no member "x", therefore the compiler rightly complains about it.
You can add the value x to the object A instead of the class/type A by doing the following:
case class A(s:String)
object A { val x = "hello" }
From the small amount you described of the problem, it sounds like case classes are just not for you.
Alternate patterns include...
Constants:
val Abs1 = "1" //note that it starts with an uppercase letter
val s: String = ...
s match {
case Abs1 => ...
case _ =>
}
Extractors:
object Positive {
def unapply(i: Int): Option[Int] = if(i >= 0) Some(i) else None
}
val i: Int = ...
i match {
case Positive(p) => ... //p will be bound to the matched positive number
case _ => ...
}
Case Classes (used properly):
case class MyType(s: String)
val x: MyType = ...
x match {
case MyType("a") => ...
case MyType("b") => ...
case MyType(matched) => ...
//matched will be bound to whatever string was used to construct the MyType instance
}
Case Objects:
abstract sealed trait Foo { def s: String }
case object Bar extends Foo { val s = "I'm a Bar" }
case object Baz extends Foo { val s = "I'm a Baz" }
val x: Foo = ...
x match {
case Bar => ...
case Baz => ...
//no other possibilities because Foo is sealed
}
Leaving aside issues of design for a moment.
If you need a workaround then you can bind an identifier to a matched case class or case object and use the bound identifier to access members of the matched entity. The bound identifier can be used in guard statements in the match or in the action provided for the match:
case class Bob(x: String, y: String) {val z = "Bragging"}
val bob1 = Bob("Big", "Bad")
bob1 match {
case b # Bob(x, y) if b.z == "Bragging" => println("Got "+x+", "+y+", "+b.z+" Bob!")
case _ =>
}
case object Bob {val z = "Balding"}
val bob2 = Bob
bob2 match {
case b # Bob if b.z == "Balding" => println("Got "+b.z+" Bob!")
case _ =>
}
Returning to design, in your case class definition you declare 'name' in the constructor body of B but you would get more useability from having 'name' as a parameter:
case class B(i: Int, name: String) extends A
Which could then match like this:
def getType(s:String) = s match {
case B(_, name) => println ("Object B("+name+")")
...
Finally it's hard to say without further detail but I suspect that mapping case classes to a large set of similar entities on a one to one basis is perhaps not the best choice, better to use case objects, or instances of a limited number of case classes or even tuples.
I'm doing matching against some case classes and would like to handle two of the cases in the same way. Something like this:
abstract class Foo
case class A extends Foo
case class B(s:String) extends Foo
case class C(s:String) extends Foo
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(sb) | C(sc) => "B"
case _ => "default"
}
}
But when I do this I get the error:
(fragment of test.scala):10: error: illegal variable in pattern alternative
case B(sb) | C(sc) => "B"
I can get it working of I remove the parameters from the definition of B and C but how can I match with the params?
Looks like you don't care about the values of the String parameters, and want to treat B and C the same, so:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(_) | C(_) => "B"
case _ => "default"
}
}
If you must, must, must extract the parameter and treat them in the same code block, you could:
def matcher(l: Foo): String = {
l match {
case A() => "A"
case bOrC # (B(_) | C(_)) => {
val s = bOrC.asInstanceOf[{def s: String}].s // ugly, ugly
"B(" + s + ")"
}
case _ => "default"
}
}
Though I feel it would be much cleaner to factor that out into a method:
def doB(s: String) = { "B(" + s + ")" }
def matcher(l: Foo): String = {
l match {
case A() => "A"
case B(s) => doB(s)
case C(s) => doB(s)
case _ => "default"
}
}
There are a couple of ways that I can see to achieve what you are after, if you have some commonality between case classes. The first is to have the case classes extend a trait which declares the commonality, the second is to use a structural type which removes the need to extend your case classes.
object MuliCase {
abstract class Foo
case object A extends Foo
trait SupportsS {val s: String}
type Stype = Foo {val s: String}
case class B(s:String) extends Foo
case class C(s:String) extends Foo
case class D(s:String) extends Foo with SupportsS
case class E(s:String) extends Foo with SupportsS
def matcher1(l: Foo): String = {
l match {
case A => "A"
case s: Stype => println(s.s); "B"
case _ => "default"
}
}
def matcher2(l: Foo): String = {
l match {
case A => "A"
case s: SupportsS => println(s.s); "B"
case _ => "default"
}
}
def main(args: Array[String]) {
val a = A
val b = B("B's s value")
val c = C("C's s value")
println(matcher1(a))
println(matcher1(b))
println(matcher1(c))
val d = D("D's s value")
val e = E("E's s value")
println(matcher2(d))
println(matcher2(e))
}
}
The structural type method generates a warning about erasure which, at present I'm not sure how to eliminate.
Well, it doesn't really make sense, does it? B and C are mutually exclusive, so either sb or sc get bound, but you don't know which, so you'd need further selection logic to decide which to use (given that they were bound to a Option[String], not a String). So there's nothing gained over this:
l match {
case A() => "A"
case B(sb) => "B(" + sb + ")"
case C(sc) => "C(" + sc + ")"
case _ => "default"
}
Or this:
l match {
case A() => "A"
case _: B => "B"
case _: C => "C"
case _ => "default"
}