scala 迭代案例类数据成员
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17826599/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iterate over case class data members
提问by Ehud Kaldor
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).
我正在用 mongodb 编写 play2.1 应用程序,我的模型对象有点广泛。在更新数据库中的条目时,我需要将来自表单的临时对象与数据库中的内容进行比较,以便我可以构建更新查询(并记录更改)。
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).
我正在寻找一种方法来一般地采用 2 个实例并获得它们的差异。迭代每个数据成员很长,硬编码并且容易出错(如果 a.firstName.equalsIgnoreCase(b.firstName)),所以我正在寻找一种方法来迭代所有数据成员并水平比较它们(名称映射 - > value 可以,或者我可以信任的列表每次都以相同的顺序枚举数据成员)。
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...
以下所有三个解决方案都很棒,但我仍然无法获得该字段的名称,对吗?这意味着我可以记录更改,但不能记录它影响的字段......
采纳答案by Shadowlands
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:
扩展@Malte_Schwerhoff 的答案,您可能会创建一个递归 diff 方法,该方法不仅生成差异索引,而且将它们映射到该索引处的新值 - 或者在嵌套产品类型的情况下,子产品的映射区别:
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
回答by Eastsun
Maybe productIteratoris what you wanted:
也许productIterator是你想要的:
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)
回答by Malte Schwerhoff
You can use the product iterator, and match on the elements if you want to use non-standard equality such as String.equalsIgnoreCase.
如果您想使用非标准等式,例如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

