2013-07-24 10 views
12

Piszę aplikację play2.1 z mongodb, a mój obiekt modelu jest nieco obszerny. przy aktualizacji wpisu w DB, muszę porównać obiekt temp przychodzący z formularza z tym, co jest w DB, więc mogę zbudować zapytanie aktualizacji (i zapisać zmiany).iteracja nad członkami danych klasy case

Szukam sposobu, aby ogólnie wziąć 2 wystąpienia i uzyskać różnicę z nich. Iterowanie nad każdym elementem danych jest długie, zakodowane i podatne na błędy (jeśli a.firstName.equalsIgnoreCase (b.firstName)), więc szukam sposobu na iterację wszystkich elementów danych i porównanie ich w poziomie (mapa nazwy - > wartość zostanie wykonana lub lista, której mogę zaufać, aby wyliczyć elementy danych w tej samej kolejności za każdym razem).

jakieś pomysły? poniżej

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

wszystkie trzy rozwiązania są świetne, ale nadal nie mogę dostać nazwę dziedzinie, prawda? To znaczy, że można rejestrować zmiany, ale nie wpływa to, co pole ...

+0

Czy możesz zamieścić kilka fragmentów kodu? Twoje klasy przypadków, przykłady przypadków i wynik, który chcesz osiągnąć? –

Odpowiedz

8

poszerzając @ odpowiedź Malte_Schwerhoff użytkownika, można potencjalnie stworzyć rekurencyjną metodę diff, że nie tylko wygenerowane indeksy różnic, lecz odwzorowany je do nowej wartości w tym indeksie - lub w przypadku zagnieżdżonych typów produktów, mapa różnic sub-produktu:

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 
} 

poszerzając przypadków użycia z tej odpowiedzi:

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

Może productIterator jest to, czego chciał:

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

Można użyć iterator produktu, a mecz na elementach jeśli chcesz używać niestandardowej równości, takiej jak 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 
} 

Przypadki użycia:

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