Uniunea exterioară completă la Scala

Având în vedere o listă de liste, în care fiecare listă are un obiect care reprezintă cheia, trebuie să scriu o îmbinare exterioară completă care combină toate listele. Fiecare înregistrare din lista rezultată este o combinație a tuturor câmpurilor tuturor listelor. În cazul în care o cheie este prezentă în lista 1 și nu este prezentă în lista 2, atunci câmpurile din lista 2 ar trebui să fie nule sau goale.

O soluție la care m-am gândit este să încorporez o bază de date în memorie, să creez tabelele, să rulez un select și să obțin rezultatul. Cu toate acestea, aș dori să știu dacă există biblioteci care gestionează acest lucru într-un mod mai simplu. Vreo idee?

De exemplu, să presupunem că am două liste, unde cheia este primul câmp din listă:

val list1 = List ((1,2), (3,4), (5,6))
val list2 = List ((1,"A"), (7,"B"))
val allLists = List (list1, list2)

Lista completă exterioară unită ar fi:

val allListsJoined = List ((1,2,"A"), (3,4,None), (5,6,None), (7,None,"B"))

NOTĂ: soluția trebuie să funcționeze pentru N liste


person ps0604    schedule 09.04.2018    source sursă
comment
Întrebarea ta nu este suficient de clară, cel puțin pentru mine. Ce este un câmp dintr-o listă? Cum le combini? Vă rugăm să oferiți un exemplu   -  person Euge    schedule 09.04.2018
comment
A adăugat exemplul   -  person ps0604    schedule 09.04.2018
comment
Ce ai încercat deja ca cod?   -  person cchantep    schedule 09.04.2018
comment
Puteți încerca să transformați lista în hărți, folosind groupBy(_._1)   -  person Euge    schedule 09.04.2018


Răspunsuri (2)


def fullOuterJoin[K, V1, V2](xs: List[(K, V1)], ys: List[(K, V2)]): List[(K, Option[V1], Option[V2])] = {
  val map1 = xs.toMap
  val map2 = ys.toMap
  val allKeys = map1.keySet ++ map2.keySet
  allKeys.toList.map(k => (k, map1.get(k), map2.get(k)))
}

Exemplu de utilizare:

val list1 = List ((1,2), (3,4), (5,6))
val list2 = List ((1,"A"), (7,"B"))
println(fullOuterJoin(list1, list2))

Care imprimă:

List((1,Some(2),Some(A)), (3,Some(4),None), (5,Some(6),None), (7,None,Some(B)))

Editați după sugestie în comentarii:

Dacă sunteți interesat să vă alăturați unui număr arbitrar de liste și nu vă pasă de informațiile de tip, iată o versiune care face asta:

def fullOuterJoin[K](xs: List[List[(K, Any)]]): List[(K, List[Option[Any]])] = {
  val maps = xs.map(_.toMap)
  val allKeys = maps.map(_.keySet).reduce(_ ++ _)
  allKeys.toList.map(k => (k, maps.map(m => m.get(k))))
}

val list1 = List ((1,2), (3,4), (5,6))
val list2 = List ((1,"A"), (7,"B"))
val list3 = List((1, 3.5), (7, 4.0))
val lists = List(list1, list2, list3)
println(fullOuterJoin(lists))

care iese:

List((1,List(Some(2), Some(A), Some(3.5))), (3,List(Some(4), None, None)), (5,List(Some(6), None, None)), (7,List(None, Some(B), Some(4.0))))

Dacă doriți atât un număr arbitrar de liste, cât și rezultate bine tastate, probabil că acesta depășește scopul unui răspuns stackoverflow, dar probabil că ar putea fi realizat cu formă.

person Joe K    schedule 09.04.2018
comment
Aceasta funcționează cu două liste de perechi. Acestea sunt două restricții suplimentare. Am înțeles că OP a vrut un număr arbitrar de liste cu tupluri de diferite arități. - person Andrey Tyukin; 09.04.2018
comment
Hmm, da, cred că interpretarea ta ar putea fi corectă. Dar se pare că trecerea de la asta la mai multe liste sau tupluri de aritate superioară nu ar trebui să fie prea greu. Îmbinarea completă exterioară a N liste se poate face prin unirea completă exterioară a două liste de N-1 ori (și făcând o grămadă de despachetare a tuplurilor imbricate). A face acest lucru într-un mod mai general, fără a pierde informațiile de tip, probabil necesită lipsă de formă și este semnificativ mai greu. - person Joe K; 09.04.2018
comment
Din anumite motive, am impresia că oamenilor cărora le pasă de îmbinările exterioare, de obicei, nu le pasă deloc de Shapeless. De exemplu, Apache Spark este complet în regulă cu aruncarea tuturor informațiilor de tip pe fereastră. A face acest lucru pentru List[List[Any]] nu ar trebui să fie prea greu, dar din nou: nu văd cum se poate face acest lucru fără un fel de schemă externă care nu trebuie să fie dedusă din conținutul tuplurilor. - person Andrey Tyukin; 09.04.2018
comment
Pentru a fi corect să declanșeze, API-ul RDD de nivel inferior realizează îmbinări tipizate; semnătura metodei este în esență identică cu ceea ce am dat în acest răspuns. Dar da, setul de date/sql API-ul este îngrozitor de netipizat. - person Joe K; 09.04.2018

Iată o modalitate de a face acest lucru folosind collect separat în ambele liste

val list1Ite =  list1.collect{
  case ele if list2.filter(e=> e._1 == ele._1).size>0 => { //if list2 _1 contains ele._1
     val left = list2.find(e=> e._1 == ele._1) //find the available element
     (ele._1, ele._2, left.get._2) //perform join
  }
  case others => (others._1, others._2, None) //others add None as _3 
}
//list1Ite: List[(Int, Int, java.io.Serializable)] = List((1,2,A), (3,4,None), (5,6,None))

Efectuați o operație similară, dar excludeți elementele care sunt deja disponibile în list1Ite

val list2Ite = list2.collect{
  case ele if list1.filter(e=> e._1 == ele._1).size==0 => (ele._1, None , ele._2)
}
//list2Ite: List[(Int, None.type, String)] = List((7,None,B))

Combinați ambele list1Ite și list2Ite la result

val result = list1Ite.++(list2Ite)

result: List[(Int, Any, java.io.Serializable)] = List((1,2,A), (3,4,None), (5,6,None), (7,None,B))
person Puneeth Reddy V    schedule 10.04.2018
comment
Soluția ta funcționează pentru 2 liste, dar am nevoie să funcționeze pentru N liste - person ps0604; 10.04.2018