I have been reading in some thread how to map a case class to a Map in scala. Unfortunately none of the answers provided are good enough.
My example would be to have something like:
case class A(a1: String, a2: String, a3: C)
case class C(c1: String, c2: String, c3: D)
case class D(d1: String, d2: String)
Assuming that we have the following instances
val d = D(d1 ="valueForD1", d2 = "valueForD2")
val c = C(c1 ="valueForc1", c2 = "valueForC2", c3 = d)
val a = A(a1 ="valueForA1", a2 = "valueForA2", a3 = c)
I want to transform that case class graph into the following form:
Map("a1" -> "valueForA1", "a2" -> "valueForA2",
"a3" -> Map("c1" -> "valueForC1", "c2" -> "valueForc2",
"c3" -> Map("d1" -> "valueForD1", "d2" -> "valueFord2")))
This is a most direct approach, not very scalable, though...
def toGraph(a: A): Map[String, Any] =
a match {
case A(a1,a2,C(c1,c2,D(d1,d2))) => Map(
"a1" -> a1,
"a2" -> a2,
"a3" -> Map(
"c1" -> c1,
"c2" -> c2,
"c3" -> Map(
"d1" -> d1,
"d2" -> d2)
)
)
case _ => Map()
}
I suppose that for a more "dynamic" approach you can resort to macros
Related
I'm new in Scala, so struggling to understand Syntax. Please check below code.
def myDef(entityMap: Seq[(DataName.Value, DataFrame)]) : Seq[Map[Int,Info]] = {
val depenInfo = Seq[Map[Int,Info]]()
entityMap.foldLeft(depenInfo)((info,entity) => {
val(dataName: DataName.Value, df: DataFrame) = entity
info ++ df.createDepenInfo(dataName)
})
}
what am I getting is, Seq of tuples having to two types (DataName.Value, DataFrame) and return type of myDef is Seq of Map.
After that, create one empty Seq of Map, then feed this empty Map to entityMap.foldLeft so that it could add more values in it.
Remaining part I kind of literally stuck to understand. Can anyone please help me to understand what's happening ? If possible with any very simple example similar to above with output.
Thanks
Since there are many user defined class I don't know, I try to mock your data type as following:
import scala.collection.{immutable, Seq}
object Example {
object DataName {
type Value = Int
}
case class DataFrame(fakeData: String) {
def createDepenInfo(value: DataName.Value): Seq[Map[Int, Info]] = Seq(Map(value -> fakeData))
}
type Info = String
def myDef(entityMap: Seq[(DataName.Value, DataFrame)]): Seq[Map[Int, Info]] = {
val depenInfo = Seq[Map[Int, Info]]()
entityMap.foldLeft(depenInfo)((info: Seq[Map[Int, Info]], entity: (DataName.Value, DataFrame)) => {
// here is Pattern matching on tuples,
// here we extract (dataName: DataName.Value, df: DataFrame) from tuple entity: (DataName.Value, DataFrame)
// see: https://docs.scala-lang.org/tour/tuples.html
val (dataName: DataName.Value, df: DataFrame) = entity
// ++ is a method of Seq, it contact two Seq to one
// e.g. Seq(1,2,3) ++ Seq(4,5,6) = Seq(1,2,3,4,5,6)
info ++ df.createDepenInfo(dataName)
})
}
def main(args: Array[String]): Unit = {
val data: immutable.Seq[(DataName.Value, DataFrame)] = (1 to 5).map(i => (i, DataFrame((i + 'a').toChar.toString)))
// Vector((1,DataFrame(b)), (2,DataFrame(c)), (3,DataFrame(d)), (4,DataFrame(e)), (5,DataFrame(f)))
println(data)
val res = myDef(data)
// List(Map(1 -> b), Map(2 -> c), Map(3 -> d), Map(4 -> e), Map(5 -> f))
println(res)
}
}
raw data: Vector((1,DataFrame(b)), (2,DataFrame(c)), (3,DataFrame(d)), (4,DataFrame(e)), (5,DataFrame(f)))
let's say info ++ df.createDepenInfo(dataName) is result
info = Seq(), entity = (1,DataFrame(b)), reuslt=Seq(Map(1 -> b))
info = Seq(Map(1 -> b)), entity = (2,DataFrame(c)), result=Seq(Map(1 -> b), Map(2 -> c))
info = Seq(Map(1 -> b), Map(2 -> c)), entity = (3,DataFrame(d)), result=Seq(Map(1 -> b), Map(2 -> c), Map(3 -> d))
and so on...
You see, during each caluclation, the value info is "saved"(with a init value from deepInfo), and the entity value is "read" from entityMap.
So the final result is List(Map(1 -> b), Map(2 -> c), Map(3 -> d), Map(4 -> e), Map(5 -> f))
In your code, info is the accumulator, depenInfo is the initial value, and entity is a map item (i.e. a key-value tuple). Here's a simpler example where acc is the accumulator and kv is the key-value pair being "read" from the map.
Map(1->2).foldLeft(0)((acc, kv) => {
val (k, v) = kv;
println(s"$k, $v, $acc");
acc + k + v
})
// prints: 1, 2, 0
// result: 3
To read about the accumulator pattern: https://www.arothuis.nl/posts/accumulators-and-folds/
As for the ++, that is the operator/method in Seq (sequence) which concatenates this sequence to another sequence. Simple example with concatenating length 1 sequences together:
Seq(1) ++ Seq(2)
// Seq(1, 2)
I need to get an object User(1,Nick Holland,25,None) from the List(Map()), but I can't understand how
val a = request.body.asFormUrlEncoded.toSeq.map(a => a.map(b => b))
List(Map(name -> ArrayBuffer(), age -> ArrayBuffer(), deleteItem -> ArrayBuffer(User(1,Nick Holland,25,None)), action -> ArrayBuffer(remove)))
Try
case class User(i: Int, str: String, i1: Int, opt: Option[String])
val l = List(Map("name" -> ArrayBuffer(), "age" -> ArrayBuffer(), "deleteItem" -> ArrayBuffer(User(1,"Nick Holland",25,None)), "action" -> ArrayBuffer("remove")))
l.head.apply("deleteItem").head //User(1,Nick Holland,25,None)
I am pretty much new to Scala and trying to learn some stuff and would like to understand the best possible approach on below:
I have a list of maps as below.
List<Map<String,String>> listOfMaps = new ArrayList<>();
Map<String,String> map1 = new HashMap<>();
map1.put("a","A1");
map1.put("b","B1");
.
.
.
map10.put("a","A10");
map10.put("b","B10");
listOfMaps.add(map1);
listOfMaps.add(map2);
...
listOfMaps.add(map10);
I would like to iterate this and transform in to another list with just a certain key, value pair from the map and add a new value
List<Map<String,String>> newlistOfMaps = new ArrayList<>();
for(Map map: listOfMaps) {
Map<String,String> newMap = new HashMap<>();
newMap.put("a_b", map.get("a")+"_"+map.get("b"));
newListOfMaps.add(newMap);
}
System.out.println(newListOfMaps);
What is the best-practice to follow while doing this in Scala? Any help or suggestion would be great!
Does this look like what you're seeking?
Pay close attention to the syntax - you will notice it's far easier to construct a List and Map in Scala than in Java.
scala> val listOfMaps: List[Map[String, String]] = List(
Map("a" -> "A1", "b" -> "B1"),
Map("a" -> "A2", "b" -> "B2"),
Map("a" -> "A3", "b" -> "B3")
)
listOfMaps: List[Map[String,String]] = List(Map(a -> A1, b -> B1), Map(a -> A2, b -> B2), Map(a -> A3, b -> B3))
scala> val mapsCombined: List[Map[String, String]] = listOfMaps.map{ m =>
Map("a_b" -> s"${m.apply("a")}_${m.apply("b")}") // string interpolation to avoid String concatenation
}
mapsCombined: List[Map[String,String]] = List(Map(a_b -> A1_B1), Map(a_b -> A2_B2), Map(a_b -> A3_B3))
For mapsCombined we can do
listOfMaps.map{ m =>
Map("a_b" -> s"${m("a")}_${m("b")}")
}
// no .apply as calling with the paren directly is it's equivalent in Scala, also the same as .get on Java.
Use Scala's map function to transform the list elements (i.e. Maps) into their corresponding concatenated keys-values:
scala> val mapList: List[Map[String, String]] = List(
Map("a" -> "A1", "b" -> "B1"), Map("a" -> "A2", "b" -> "B2"), Map("a" -> "A3", "b" -> "B3")
)
mapList: List[Map[String,String]] = List(Map(a -> A1, b -> B1), Map(a -> A2, b -> B2), Map(a -> A3, b -> B3))
scala> val newMapList: List[Map[String, String]] = mapList.map( m =>
Map(m.keys.mkString("_") -> m.values.mkString("_"))
)
newMapList: List[Map[String,String]] = List(Map(a_b -> A1_B1), Map(a_b -> A2_B2), Map(a_b -> A3_B3))
Note that the same function will work if you decide to expand the original Maps to, say:
Map("a" -> "A1", "b" -> "B1", c -> "C1", ...),
Map("a" -> "A2", "b" -> "B2", c -> "C2", ...),
...
Is it possible to create an object from a Map[String, Any] where each pair is a (StringField -> Value) ?
For example,
case class Example(a: String, b: Int)
val obj = new Example( Map('a' -> 'blah', 'b' -> 1) ) //?
val orMaybe = Example( Map('a' -> 'blah', 'b' -> 1 ) //?
You can pass a tuple as the parameter list to a function using .tupled:
(Example.apply _).tupled("a" -> 1)
With that, and knowing that myMap.map passes each element of the Map as a Tuple, you could do the following:
Map("a" -> 1).map((Example.apply _).tupled).head
Yes, it's possible in several ways. One of them would be to use reflection, another (a simpler one) would be to use json as an intermediate.
E.g. with json4s:
import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read, write}
scala> implicit val formats = DefaultFormats
formats: org.json4s.DefaultFormats.type = org.json4s.DefaultFormats$#1f2bf363
case class Example(a: String, b: Int)
scala> val obj = read[Example](write( Map("a" -> "blah", "b" -> 1) ))
obj: Example = Example(blah,1)
scala: val orMaybe = read[Example](write( Map("a" -> "blah", "b" -> 1 )))
orMaybe: Example = Example(blah,1)
I am writing a play2.1 application with mongodb, and my model object is a bit extensive. when updating an entry in the DB, i need to compare the temp object coming from the form with what's in the DB, so i can build the update query (and log the changes).
i am looking for a way to generically take 2 instances and get a diff of them. iterating over each data member is long, hard-coded and error prone (if a.firstName.equalsIgnoreCase(b.firstName)) so i am looking for a way to iterate over all data members and compare them horizontally (a map of name -> value will do, or a list i can trust to enumerate the data members in the same order every time).
any ideas?
case class Customer(
id: Option[BSONObjectID] = Some(BSONObjectID.generate),
firstName: String,
middleName: String,
lastName: String,
address: List[Address],
phoneNumbers: List[PhoneNumber],
email: String,
creationTime: Option[DateTime] = Some(DateTime.now()),
lastUpdateTime: Option[DateTime] = Some(DateTime.now())
)
all three solutions below are great, but i still cannot get the field's name, right? that means i can log the change, but not what field it affected...
Maybe productIterator is what you wanted:
scala> case class C(x: Int, y: String, z: Char)
defined class C
scala> val c1 = C(1, "2", 'c')
c1: C = C(1,2,c)
scala> c1.productIterator.toList
res1: List[Any] = List(1, 2, c)
Expanding on #Malte_Schwerhoff's answer, you could potentially create a recursive diff method that not only generated the indexes of differences, but mapped them to the new value at that index - or in the case of nested Product types, a map of the sub-Product differences:
def diff(orig: Product, update: Product): Map[Int, Any] = {
assert(orig != null && update != null, "Both products must be non-null")
assert(orig.getClass == update.getClass, "Both products must be of the same class")
val diffs = for (ix <- 0 until orig.productArity) yield {
(orig.productElement(ix), update.productElement(ix)) match {
case (s1: String, s2: String) if (!s1.equalsIgnoreCase(s2)) => Some((ix -> s2))
case (s1: String, s2: String) => None
case (p1: Product, p2: Product) if (p1 != p2) => Some((ix -> diff(p1, p2)))
case (x, y) if (x != y) => Some((ix -> y))
case _ => None
}
}
diffs.flatten.toMap
}
Expanding on the use cases from that answer:
case class A(x: Int, y: String)
case class B(a: A, b: AnyRef, c: Any)
val a1 = A(4, "four")
val a2 = A(4, "Four")
val a3 = A(4, "quatre")
val a4 = A(5, "five")
val b1 = B(a1, null, 6)
val b2 = B(a1, null, 7)
val b3 = B(a2, a2, a2)
val b4 = B(a4, null, 8)
println(diff(a1, a2)) // Map()
println(diff(a1, a3)) // Map(0 -> 5)
println(diff(a1, a4)) // Map(0 -> 5, 1 -> five)
println(diff(b1, b2)) // Map(2 -> 7)
println(diff(b1, b3)) // Map(1 -> A(4,four), 2 -> A(4,four))
println(diff(b1, b4)) // Map(0 -> Map(0 -> 5, 1 -> five), 2 -> 8l
You can use the product iterator, and match on the elements if you want to use non-standard equality such as String.equalsIgnoreCase.
def compare(p1: Product, p2: Product): List[Int] = {
assert(p1 != null && p2 != null, "Both products must be non-null")
assert(p1.getClass == p2.getClass, "Both products must be of the same class")
var idx = List[Int]()
for (i <- 0 until p1.productArity) {
val equal = (p1.productElement(i), p2.productElement(i)) match {
case (s1: String, s2: String) => s1.equalsIgnoreCase(s2)
case (x, y) => x == y
}
if (!equal) idx ::= i
}
idx.reverse
}
Use cases:
case class A(x: Int, y: String)
case class B(a: A, b: AnyRef, c: Any)
val a1 = A(4, "four")
val a2 = A(4, "Four")
val a3 = A(5, "five")
val b1 = B(a1, null, 6)
val b2 = B(a1, null, 7)
val b3 = B(a2, a2, a2)
println(compare(a1, a2)) // List()
println(compare(a1, a3)) // List(0, 1)
println(compare(b1, b2)) // List(2)
println(compare(b2, b3)) // List(0, 1, 2)
// println(compare(a1, b1)) // assertion failed
If you want to have access to the field name you can use Java's Reflection API. In this case you can access the declared fields by using the getDeclaredFields method and iterate over the fields. If you then want to access the field's value changes to the field you need to set it accessible (setAccessible method) because by default all class parameters are implemented as private fields with public accessors.
val c = C(1, "C", 'c'))
for(field <- c.getClass.getDeclaredFields) {
println(field.getName)
field.get(c)
}