2013-07-24 12 views
12

私はmongodbでplay2.1アプリケーションを作成しています。私のモデルオブジェクトは多少拡張されています。私はDB内のエントリを更新するときに、私は更新のクエリを構築することができます(と変更をログに記録する)ので、フォームから来ている一時オブジェクトをDBにあるものと比較する必要があります。ケースクラスのデータメンバーを繰り返し処理する

私は一般的に2つのインスタンスを取得し、それらのdiffを取得する方法を探しています。すべてのデータメンバーを繰り返し処理する方法を探しているので、各データメンバーを繰り返し処理することは、ハードコーディングされ、エラーが発生する可能性があります(a.firstName.equalsIgnoreCase(b.firstName) >値が実行されるか、またはリストが信頼できるので、毎回同じ順序でデータメンバーが列挙されます)。

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()) 
) 

右、下の3つのすべてのソリューションは素晴らしいですが、私はまだ、フィールドの名前を取得することはできませんか?それは私が何をフィールドそれが影響を受けた変更をログに記録することはできませんが、意味...たぶん

+0

いくつかのコードスニペットを投稿できますか?ケースクラス、いくつかのサンプルインスタンス、および成果を達成したいですか? –

答えて

8

、あなたは潜在的な違いのインデックスを生成しますが、そのインデックスに新しい値にそれらをマッピングされていないだけで、再帰的差分法を作成することができます - またはネストされた製品の種類、サブプロダクト差のマップの場合:その答えからユースケースを拡張し

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 
} 

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 
24

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) 
5

したい場合は、要素で製品のイテレータ、および一致を使用することができます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 
} 

ユースケース:Malte_Schwerhoffの答え@上の拡大

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 
関連する問題