Scala - Check If object exist inside a list - scala

I'm trying to write an algorithm to check if an object exists inside a list.
My case class is the following:
case class Person(id:Int, name:String, friends:List[Person] = Nil)
I've wrote it using this code:
#tailrec
final def find(id: Int, tree: List[Person]):Option[Person] = tree match {
case Nil => None
case (h :: t) => if(h.id == id) Option(h) else find(key, t ::: h.friends)
}
Is it a good approach? Use tail recursive and append another list on tail list? If not, what would be the best approach?

I ended up with the following implementation which basically tail recursive BFS:
#tailrec
def find(id: Int, level: List[Person], visited: Set[Person] = Set.empty): Option[Person] =
if (level.isEmpty) None
else {
// Try to find person on this level
val found = level.find(_.id == id).filterNot(visited.contains)
if (found.isDefined) found
else find(
id,
// Next level construction
level.flatMap(_.friends).distinct,
// Keep track of visited to handle cycles
visited ++ level
)
}
UPDATE: Also, I would suggest to add call by name to be able to play around with more test cases:
class Person(val id: Int, val name: String, friends: => List[Person]) {
def friendList = friends
override def toString = name
}
object Person {
def apply(id: Int, name: String, friends: => List[Person] = Nil) = new Person(id, name, friends)
}
In that case you can compose the example from the comments:
val john = Person(1, "john")
val russ = Person(2, "russ")
val bob = Person(3, "bob")
val lois = Person(5, "lois")
lazy val `eve friends` = List(john, anne)
lazy val `peter friends` = List(eve, bob, russ)
lazy val `anne friends` = List(peter, lois)
val eve = Person(7, "eve", `eve friends`)
val peter = Person(8, "peter", `peter friends`)
val anne: Person = Person(6, "anne", `anne friends`)
println(find(8, List(eve, peter, lois))) // result: Some(peter)

Related

Scala/Java : Compare two list/set and removed matched element from both list

I have two lists with two different types, but both types have the same field to identify/compare.
My Requirement: I want to compare two lists based on some fields of objects, once matched delete element from both list/set.
For example:
case class Type1(name:String, surname: String, address: Int)
case class Type2(name:String, surname: String, address: Int, dummy: String)
So record will be matched if both lists have the same field data for both types.
My List:
val type1List = List(Type1("name1","surname1", 1),
Type1("name2","surname2", 2),
Type1("name3","surname3", 3)
)
val type2List = List(Type2("name1","surname1", 1),
Type2("name2","surname2", 2),
Type2("name4","surname4", 4)
)
Comparing type1List and type2List, removed the matched date from both lists.
type1List should contain only:
val type1List = List(
Type1("name3","surname3", 3)
)
type2List should contain only:
val type2List = List(
Type2("name4","surname4", 4)
)
I tried it with looping/interation , but that seems too complex and performance hit.
Thanks in advance.
Here is a generic approach to the task.
The idea is to find the common elements in both lists according to some custom functions, and then remove both of them.
def removeCommon[A, B, K](as: List[A], bs: List[B])
(asKey: A => K)
(bsKey: B => K): (List[A], List[B]) = {
def removeDuplicates[V](commonKeys: Set[K], map: Map[K, List[V]]): List[V] =
map
.iterator
.collect {
case (key, value) if (!commonKeys.contains(key)) =>
value.head
}.toList
val asByKey = as.groupBy(asKey)
val bsByKey = bs.groupBy(bsKey)
val commonKeys = asByKey.keySet & bsByKey.keySet
val uniqueAs = removeDuplicates(commonKeys, asByKey)
val uniqueBs = removeDuplicates(commonKeys, bsByKey)
(uniqueAs, uniqueBs)
}
Which you can use like following:
final case class Type1(name:String, surname: String, address: Int)
final case class Type2(name:String, surname: String, address: Int, dummy: String)
val type1List = List(
Type1("name1","surname1", 1),
Type1("name2","surname2", 2),
Type1("name3","surname3", 3)
)
val type2List = List(
Type2("name1","surname1", 1, "blah"),
Type2("name2","surname2", 2, "blah"),
Type2("name4","surname4", 4, "blah")
)
val (uniqueType1List, uniqueType2List) =
removeCommon(type1List, type2List) { type1 =>
(type1.name, type1.surname, type1.address)
} { type2 =>
(type2.name, type2.surname, type2.address)
}
// uniqueType1List: List[Type1] = List(Type1("name3", "surname3", 3))
// uniqueType2List: List[Type2] = List(Type2("name4", "surname4", 4, "blah"))
If you can not adjust the 2 Types, here a pragmatic solution:
First find the 'equals'
val equals: Seq[Type1] = type1List.filter {
case Type1(n, sn, a) =>
type2List.exists { case Type2(n2, sn2, a2, _) =>
n == n2 && sn == sn2 && a == a2
}
}
Then filter both Lists:
val filteredType1List = type1List.filterNot(t1 => equals.contains(t1))
val filteredType2List = type2List.filterNot {
case Type2(n2, sn2, a2, _) =>
equals.exists { case Type1(n, sn, a)=>
n == n2 && sn == sn2 && a == a2
}
}
Assuming you know what field you want to use to base your diff on, here is an outline of a solution.
First define a super class for both types:
abstract class Type(val name : String) {
def canEqual(a: Any) = a.isInstanceOf[Type]
override def equals(obj: Any): Boolean = obj match {
case obj : Type => obj.canEqual(this) && this.name == obj.name
case _ => false
}
override def hashCode(): Int = this.name.hashCode
}
Then define your types as sub types of the above class:
case class Type1(override val name: String, surname: String, address: Int) extends Type(name)
case class Type2(override val name: String, surname: String, address: Int, dummy: String) extends Type(name)
Now a simple
type1List.diff(type2List)
will result in:
List(Type1(name3,surname3,3))
and
type2List.diff(type1List)
will give:
List(Type2(name4,surname4,4,dum3))
Still, I would be careful with solutions like this. Because circumventing equals and hashcode opens up the code to all kinds of bugs. So it is better to make sure this is kept limited to the scope you are working in.

scala function to compare fields of case class and collect the diff

I have 2 case classes:
case class Person(fname: String, lname: String, age: Int, address: String)
case class PersonUpdate(fname: String, lname: String, age: Int)
so PersonUpdate dosent have all the fields Person have, and I want to write effective that get Person and PersonUpdate and find the fields that have different values:
for example:
def findChangedFields(person: Person, personUpdate: PersonUpdate): Seq[String] = {
var listOfChangedFields: List[String] = List.empty
if (person.fname == personUpdate.fname)
listOfChangedFields = listOfChangedFields :+ "fname"
if (person.lname == personUpdate.lname)
listOfChangedFields = listOfChangedFields :+ "lname"
if (person.age == personUpdate.age)
listOfChangedFields = listOfChangedFields :+ "age"
listOfChangedFields
}
findChangedFields(per, perUpdate)
but this is very ugly, how can I write this nicely with the magic of scala?
Something like this, maybe?
val fields = Seq("fname", "lname", "age")
val changedFields = person.productIterator
.zip(personUpdate.productIterator)
.zip(fields.iterator)
.collect { case ((a, b), name) if a != b => name }
.toList
Something like this:
case class Person(fname: String, lname: String, age: Int, address: String)
case class PersonUpdate(fname: String, lname: String, age: Int)
def findFirstNameChanged(person: Person, personUpdate: PersonUpdate): List[String] =
{
if (person.fname == personUpdate.fname) List("fname")
else Nil
}
def findLastNameChanged(person: Person, personUpdate: PersonUpdate): List[String] = {
if (person.lname == personUpdate.lname) List("lname")
else Nil
}
def findAgeNameChanged(person: Person, personUpdate: PersonUpdate): List[String] = {
if (person.age == personUpdate.age) List("age")
else Nil
}
def findChangedFields(person: Person, personUpdate: PersonUpdate): Seq[String] = {
findFirstNameChanged(person,personUpdate):::
findLastNameChanged(person,personUpdate) ::: findAgeNameChanged(person,personUpdate)
}
val per = Person("Pedro","Luis",22,"street")
val personUpdate = PersonUpdate("Pedro", "Luis",27)
findChangedFields(per, personUpdate)
I think your problem is similar to compare two Set of tuple. Please feel free two correct me.
So here is my solution which will work for any two case class having field names in any order
def caseClassToSet[T](inp: T)(implicit ct: ClassTag[T]): Set[(String, AnyRef)] = {
ct.runtimeClass.getDeclaredFields.map(f => {
f.setAccessible(true)
val res = (f.getName, f.get(inp))
f.setAccessible(false)
res
}).toSet
}
val person = Person("x", "y", 10, "xy")
val personUpdate = PersonUpdate("z","y",12)
val personParams: Set[(String, AnyRef)] = caseClassToSet(person)
val personUpdateParams: Set[(String, AnyRef)] = caseClassToSet(personUpdate)
println(personUpdateParams diff personParams)
Got help from Get field names list from case class

in scala, how to create a list of immutable objects with references among them (when instances not know in advance)?

This answer https://stackoverflow.com/a/41717310/280393 explains how to create a list of immutable objects with references among them; However, the solution provided requires the objects to be known in advance.
How to achieve this when the objects are created on demand?
case class PersonA(id: Int, name: String, friends: Set[Int])
val john = PersonA(0, "john", Set(1,2))
val maria = PersonA(1, "maria", Set(0))
val georges = PersonA(2, "georges", Set(1))
val peopleA = Set(john, maria, georges)
case class PersonB(id: Int, name: String, friends: Set[PersonB])
// case class PersonB(id: Int, name: String, friends: () => Set[PersonB])
def convert(peopleA: Set[PersonA]): Set[PersonB] = ???
val peopleB = convert(peopleA)
println(peopleB)
println(peopleB.toList.map(_.friends.size))
peopleB.toList.map {
case PersonB(id, name, friends) => friends.size
}.foreach(println)
So, without modifying the implementation of case class PersonA and val peopleA, how to implement convert?
assuming that two PersonB instances are equal iff their id is equal,
one solution would be like this:
class PersonB(val id: Int, val name: String) {
var friends0: Set[PersonB] = _
def setFriends(friends: Set[PersonB]) {
require(friends0 == null)
friends0 = friends
}
def friends: Set[PersonB] = {
require(friends0 != null)
friends0
}
override def equals(that: Any): Boolean = that match {
case t: PersonB => t.id == id
case _ => false
}
override def hashCode(): Int = id.hashCode
override def toString = s"PersonB($id, $name, List(${friends.map(_.id).mkString(", ")}))"
}
object PersonB {
def apply(id: Int, name: String) = new PersonB(id, name)
def apply(id: Int, name: String, friends: Set[PersonB]): PersonB = {
val p = new PersonB(id, name)
p.setFriends(friends)
p
}
def unapply(p: PersonB): Option[(Int, String, Set[PersonB])] =
Some((p.id, p.name, p.friends))
}
def convert(peopleA: Set[PersonA]): Set[PersonB] = {
val peopleB = peopleA.map(p => new PersonB(p.id, p.name))
val peopleBMap = peopleB.map(p => (p.id, p)).toMap
peopleA.foreach(p =>
peopleBMap(p.id).setFriends(p.friends.map(peopleBMap))
)
peopleB
}
Is there a simpler way?
Udate Solution based on #sjrd answer:
class PersonB(val id: Int, val name: String, friends0: => Set[PersonB]) {
lazy val friends: Set[PersonB] = friends0
override def equals(that: Any): Boolean = that match {
case t: PersonB => t.id == id
case _ => false
}
override def hashCode(): Int = id.hashCode
override def toString = s"PersonB($id, $name, List(${friends.map(_.id).mkString(", ")}))"
}
object PersonB {
def apply(id: Int, name: String, friends: => Set[PersonB]): PersonB =
new PersonB(id, name, friends)
def unapply(p: PersonB): Option[(Int, String, Set[PersonB])] =
Some((p.id, p.name, p.friends))
}
def convert(peopleA: Set[PersonA]): Set[PersonB] = {
lazy val peopleB: Map[Int, PersonB] =
(for (PersonA(id, name, friendsIDs) <- peopleA)
yield (id, PersonB(id, name, friendsIDs.map(peopleB)))).toMap
peopleB.values.toSet
}
If you want to create immutable data you need to have a DAG / directed acyclic graph for creation order of you objects.
I do not think that you can do this since just the first 2 records have a cycle:
val john = PersonB(0, "john", Set(maria, ...))
val maria = PersonB(1, "maria", Set(john))
john is dependent on maria and maria on john.
So john is has to be created after maria and maria be created after john.
So you might have to compromise with your immutable data structures and not use case class but maybe an assign once instead.
Using this definition of PersonB:
case class PersonB(id: Int, name: String, friends: () => Set[PersonB])
you can write:
def convert(peopleA: Set[PersonA]): Set[PersonB] = {
lazy val peopleB: Map[Int, PersonB] =
(for (PersonA(id, name, friendsIDs) <- peopleA)
yield PersonB(id, name, () => friendsIDs.map(peopleB)).toMap
peopleB.values.toSet
}
Not tested, but something close to this should work.

in Slick 3.0, how to I get from a query to a case class?

I am trying to use Slick for database in a Scala application, and running into some issues (or my misunderstandings) of how to query (find) and convert the result to a case class.
I am not mapping the case class, but the actual values, with the intent of creating the case class on the fly. so, my table is:
object Tables {
class Names(tag: Tag) extends Table[Name](tag, "NAMES") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def first = column[String]("first")
def middle = column[String]("last")
def last = column[String]("last")
def * = (id.?, first, middle.?, last) <> ((Name.apply _).tupled, Name.unapply)
}
object NamesQueries {
lazy val query = TableQuery[Names]
val findById = Compiled { k: Rep[Long] =>
query.filter(_.id === k)
}
}
}
and here is the query:
object NamesDAO {
def insertName(name: Name) {
NamesQueries.query += name.copy(id = None)
}
def findName(nameId: Long) = {
val q = NamesQueries.findById(nameId) // AppliedCompiledFunction[Long, Query[Tables.Names, Tables.Names.TableElementType, Seq],Seq[Tables.Names.TableElementType]]
val resultSeq = Database.forConfig("schoolme").run(q.result) // Future[Seq[Tables.Names.TableElementType]]
val result = resultSeq.map { r => // val result: Future[(Option[Long], String, Option[String], String) => Name]
val rr = r.map{ name => // val rr: Seq[(Option[Long], String, Option[String], String) => Name]
Name.apply _
}
rr.head
}
result
}
}
however, the findName method seems to return Future((Option[Long], String, Option[String], String) => Name) instead of a Future(Name). What am i doing wrong? Is it just a matter of just using asInstanceOf[Name]?
EDIT: expanded findName to smaller chunks with comments for each one, as sap1ens suggested.
well, i'll be damned.
following sap1ens comment above, I broke findName to multiple steps (and edited the question). but after that, i went back and gave my val an explicit type, and that worked. see here:
def findName(nameId: Long) = {
val q = NamesQueries.findById(nameId)
val resultSeq: Future[Seq[Name]] = Database.forConfig("schoolme").run(q.result)
val result = resultSeq.map { r =>
val rr = r.map{ name =>
name
}
rr.head
}
result
}
so, type inference was the (/my) culprit this time. remember, remember.

Merge two case class of same type, except some fields

If you have a case class like:
case class Foo(x: String, y: String, z: String)
And you have two instances like:
Foo("x1","y1","z1")
Foo("x2","y2","z2")
Is it possible to merge instance 1 in instance 2, except for field z, so that the result would be:
Foo("x1","y1","z2")
My usecase is just that I give JSON objects to a Backbone app through a Scala API, and the Backbone app gives me back a JSON of the same structure so that I can save/update it. These JSON objects are parsed as case class for easy Scala manipulation. But some fields should never be updated by the client side (like creationDate). For now I'm doing a manual merge but I'd like a more generic solution, a bit like an enhanced copy function.
What I'd like is something like this:
instanceFromDB.updateWith(instanceFromBackbone, excludeFields = "creationDate" )
But I'd like it to be typesafe :)
Edit:
My case class have a lot more fields and I'd like the default bevavior to merge fields unless I explicitly say to not merge them.
What you want is already there; you just need to approach the problem the other way.
case class Bar(x: String, y: String)
val b1 = Bar("old", "tired")
val b2 = Bar("new", "fresh")
If you want everything in b2 not specifically mentioned, you should copy from b2; anything from b1 you want to keep you can mention explicitly:
def keepY(b1: Bar, b2: Bar) = b2.copy(y = b1.y)
scala> keepY(b1, b2)
res1: Bar = Bar(new,tired)
As long as you are copying between two instances of the same case class, and the fields are immutable like they are by default, this will do what you want.
case class Foo(x: String, y: String, z: String)
Foo("old_x", "old_y", "old_z")
// res0: Foo = Foo(old_x,old_y,old_z)
Foo("new_x", "new_y", "new_z")
// res1: Foo = Foo(new_x,new_y,new_z)
// use copy() ...
res0.copy(res1.x, res1.y)
// res2: Foo = Foo(new_x,new_y,old_z)
// ... with by-name parameters
res0.copy(y = res1.y)
// res3: Foo = Foo(old_x,new_y,old_z)
You can exclude class params from automatic copying by the copy method by currying:
case class Person(name: String, age: Int)(val create: Long, val id: Int)
This makes it clear which are ordinary value fields which the client sets and which are special fields. You can't accidentally forget to supply a special field.
For the use case of taking the value fields from one instance and the special fields from another, by reflectively invoking copy with either default args or the special members of the original:
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
import System.{ currentTimeMillis => now }
case class Person(name: String, age: Int = 18)(val create: Long = now, val id: Int = Person.nextId) {
require(name != null)
require(age >= 18)
}
object Person {
private val ns = new java.util.concurrent.atomic.AtomicInteger
def nextId = ns.getAndIncrement()
}
object Test extends App {
/** Copy of value with non-defaulting args from model. */
implicit class Copier[A: ClassTag : TypeTag](val value: A) {
def copyFrom(model: A): A = {
val valueMirror = cm reflect value
val modelMirror = cm reflect model
val name = "copy"
val copy = (typeOf[A] member TermName(name)).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = typeOf[A] member TermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(valueMirror reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
val pmethod = typeOf[A] member p.name
if (pmethod != NoSymbol) (modelMirror reflectMethod pmethod.asMethod)()
else throw new RuntimeException("No $p on model")
}
}
val args = (for (ps <- copy.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(valueMirror reflectMethod copy)(args: _*).asInstanceOf[A]
}
}
val customer = Person("Bob")()
val updated = Person("Bobby", 37)(id = -1)
val merged = updated.copyFrom(customer)
assert(merged.create == customer.create)
assert(merged.id == customer.id)
}
case class Foo(x: String, y: String, z: String)
val foo1 = Foo("x1", "y1", "z1")
val foo2 = Foo("x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("x1", "y1", "z2")
If you change Foo later to:
case class Foo(w: String, x: String, y: String, z: String)
No modification will have to be done. Explicitly:
val foo1 = Foo("w1", "x1", "y1", "z1")
val foo2 = Foo("w2", "x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("w1", "x1", "y1", "z2")