2016-04-05 19 views
1

Ich habe zwei Sequenzen von Objekten, wo die Objekte ein gemeinsames Attribut teilen (können id nennen es)Zip spärliche Sequenzen

case class ThingA(id: Int, someAttribute: String) 
case class ThingB(id: Int, someOtherAttribute: Float) 

ich will „spärlich“ Listen verbinden, die, dass jeder ThingA.id Macht sagen, nicht übereinstimmen mit ThingB.id und umgekehrt. Die IDs sind in jeder Liste eindeutig.

Beispiel Eingabe:

val thingAs = Seq(ThingA(0, "foo"), ThingA(1, "bar")) 
val thingBs = Seq(ThingB(0, 1.0), ThingB(2, 0.3)) 

gewünschte Ausgabe:

val zipped: Seq(Tuple[Option[ThingA], Option[ThingB]]) = Seq(
    (Some(ThingA(0, "foo")), Some(ThingB(0, 1.0))), // Matching id = 0 
    (Some(ThingA(1, "bar")), None), 
    (None, Some(ThingB(2, 0.3)) 
) 

wie Meine aktuelle Versuch aussieht:

val zipped = (
    thingAs.map(a => (Some(a), thingBs.find(b => b.id == a.id))) ++ 
    thingBs.map(b => (thingAs.find(a => a.id == b.id), Some(b))) 
).distinct 

, die funktioniert, aber ich bin der Hoffnung, dass es ist ein besserer Weg.

+0

Blick ' .groupBy'. Gruppiere eins nacheinander mit "id", dann "map" das andere, um mitzumachen. – Dima

+2

@Dima, kein Punkt in der '.groupBy'? "Die IDs sind in jeder Liste eindeutig." –

+0

@ArchetypalPaul der Punkt ist Nachschlagen in einer 'Map' sind konstante Zeit – Dima

Antwort

2

Ein Weg wäre, zunächst die Vereinigung aller IDs zu berechnen und dann die entsprechenden ThingA oder ThingB Instanzen mithilfe von Lookup-Maps zu holen, wie @Dima vorgeschlagen.

val lookupThingA = thingAs.map(x => x.id -> x).toMap 
val lookupThingB = thingBs.map(x => x.id -> x).toMap 

val zipped: Seq[(Option[ThingA], Option[ThingB])] = 
    (lookupThingA.keySet | lookupThingB.keySet).map(i => (lookupThingA.get(i), lookupThingB.get(i))).toList 

Output (beachten Sie, dass Sie eine Set anstelle eines Seq haben kann):

(Some(ThingA(1,bar)),None) 
(Some(ThingA(0,foo)),Some(ThingB(0,1.0))) 
(None,Some(ThingB(2,0.3))) 

(ich lasse es als Community Wiki, wenn jemand dies will verbessern) in

+0

Eigentlich ist 'groupBy' hier nicht die beste Lösung. '.map (x => x.id -> x) .toMap' ist besser, da Sie Werte nicht in Sammlungen einbinden. – Aivean

+0

@Aivean In der Tat, danke :) –