0

Ich suche nach einer schönen Möglichkeit, die Elemente von zwei Scala Kollektionen zu vergleichen und eine dritte Kollektion basierend auf einer Bedingung zu erstellen. Ich kann das Code-Erscheinungsbild für die Leistung opfern.Vergleichen Sie alle Elemente zweier Sammlungen und produzieren Sie eine dritte

Unter der Annahme, die folgenden:

case class Item(name: String, category: String, code: String, price: Int, freeItem: Option[Item]) 

val parentItems = Seq(
    Item("name_1", "category_A", "code_A", 100, None), 
    Item("name_2", "category_B", "code_B", 100, None), 
    Item("name_3", "category_C", "code_C", 100, None) 
) 

val childItems = Seq(
    Item("name_4", "category_A", "code_A", 100, None), 
    Item("name_5", "category_C", "code_C", 100, None) 
) 

def isChild(i1: Item, i2: Item): Boolean = { 
    i1.name != i2.name && 
    i1.category == i2.category && 
    i1.code == i2.code 
} 

val parentsWithChildren: Seq[Item] = (
    for { 
    i1 <- parentItems; 
    i2 <- childItems 
    } yield { 
    if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy()))) 
    else None 
    }).filter(_.isInstanceOf[Some[_]]).map(_.get) 

Die obige Snippet wird das folgende Ergebnis produzieren, die die excpected ist:

Seq(
    Item("name_1", "category_A", "code_A", 100, 
    Some(Item("name_4", "category_A", "company_A", 100, None))), 
    Item("name_3", "category_C", "code_C", 100, 
    Some(Item("name_5", "category_C", "company_C", 100, None))) 
) 

Ich weiß, dass der obige Code Lücken hat, aber das ist in Ordnung. Was ich suche ist:

  1. Gibt es eine Weise, die ich vermeiden können, wenn (IsChild (i1, i2)) Some(i1.copy(freeItem = Some(i2.copy()))) else None und als Folge der .filter(_.isInstanceOf[Some[_]]).map(_.get)? Wäre Teilfunktion eine Option?

  2. Ich verwende hier eine verschachtelte Schleife, ich würde so etwas in Java tun. Gibt es eine andere Möglichkeit, sich ihr zu nähern? Ich dachte ich zip zuerst, aber offensichtlich funktioniert nicht, zumindest für sich allein.

Vielen Dank im Voraus!

Antwort

2

würde ich versucht sein, um so etwas zu versuchen:

val parentsWithChildren: Seq[Item] = 
    for { 
    parent <- parentItems 
    child <- childItems if isChild(parent, child) 
    } 
    yield parent.copy(freeItem = Some(child)) 

Hoffnung, das hilft!

0

In Bezug auf 1, scheint die richtige Sache Option der Rückkehr zu tun, aber Sie können die Filter mit Flatten ersetzen:

(for { 
    i1 <- parentItems; 
    i2 <- childItems 
    } yield { 
    if (isChild(i1, i2)) Some(i1.copy(freeItem = Some(i2.copy()))) 
    else None 
    }).flatten 

Was 2, wenn Sie wirklich eine verschachtelte Schleife benötigen und Sie sind besorgt über Leistung, Ihr Code sieht gut aus für mich. Vielleicht hat jemand eine bessere Idee.

+0

Danke, dass Sie sich die Zeit genommen haben, die Frage zu beantworten. "flatten" arbeiten gut und erzeugen das erwartete Ergebnis. @Martin hat es jedoch noch kompakter gemacht. Ich denke, dass beide Implementierungen mehr oder weniger das gleiche leisten sollten. – Angelos

+0

@Angelos Ja, seine Lösung ist besser, besser lesbar – nmat

0

Ein etwas unorthodoxer Ansatz, aber eine, die ich glaube, wäre mehr performant, wäre category und code verketten den Schlüssel zu einer Karte zu bilden, mit einem groupBy: Sie iterieren

val childItemMap: Map[String, Iterable[Item]] = childItems 
    .groupBy(item => s"${item.category}-${item.code}") 

Dann einfach über parentItem s, verketten ihre Kategorien und Codes, und die Kartenwerte in die Eltern kopieren:

val parentsWithChildren: Seq[Item] = parentItems 
    .map { p => 
     p.copy(freeItem = childItemMap.get(s"${p.category}-${p.code}").map(_.head)) 
    } 

Ihre Daten Das Modell legt nahe, dass es nur eine mögliche Übereinstimmung gibt, also packe ich nur den ersten (und vermutlich nur) passenden Wert. Ich verwende head anstatt headOption, weil ich die Map mit einer groupBy erzeugte, also würde es nie einen leeren Wert geben. Das Ergebnis ist ein Seq[Item] mit den untergeordneten Übereinstimmungen, die in die Eltern kopiert werden.

+0

Vielen Dank für die Bereitstellung dieser Antwort.Es funktioniert in meinem Fall ganz gut, aber ich habe etwas kompliziertere Modelle und es wird schwierig für andere Leute zu folgen. Ich habe seine Leistung nicht gemessen, aber Sie können recht damit haben :) – Angelos

+0

Das ist in Ordnung, aber ich denke immer noch, dass Sie einen 'Map'-basierten Ansatz in Betracht ziehen sollten - auch wenn es nicht wörtlich der ist, den ich beschreibe. – Vidya

+0

können Sie mir erklären warum, danke? – Angelos

Verwandte Themen