2016-05-19 4 views
1

Lassen Sie uns mit einer Folge von ganzzahligen beginnen wie:Wie man Bereiche von zusammenhängenden Inten effizient/elegant extrahiert?

val seq = List(1,2,3,4,5,6,9,10,11,14,15,16,18) 

Ich möchte eine Folge von Paaren bekommen repräsentieren zusammenhängende Sätze, wie:

val ranges = List(1,6,9,11,14,16,18,18) 

Alternative Format Seq[(Int,Int)] auch akzeptabel ist:

Erläuterung: - Ganzzahlen im Bereich 1..6 und 11..16 sind in seq - integer 18 in seq ist, hat aber keine Nachfolger oder Vorgänger, so scheint es, als 18,18 in ranges

Man beachte, daß Einzelelement-Sequenzen sollten immer paarweise berichtet werden, wie beispielsweise in:

val seq = List(18, 19, 21) 

, die als Ergebnis geben sollte:

val ranges = List(18,19,21,21) 

Oder, wenn Sie bevorzugen Tuple2 Stil:

val ranges = List((18,19),(21,21)) 

Ich möchte eine Funktion haben ranges von seq abzuleiten; eine Lösung (von einem Kollegen zur Verfügung gestellt) ist:

def toRanges(a: Seq[Int]): Seq[Int] = { 
    val min = a.map(x => (x, a contains x - 1)).filter(!_._2).map(_._1) 
    val max = a.map(x => (x, a contains x + 1)).filter(!_._2).map(_._1) 
    return (min ++ max).sorted 
} 

Welche elegant ist, in der Tat, aber ich bin mir nicht sicher über die Effizienz, von contains aufgrund verwenden.

Kann jemand eine noch bessere Lösung in Bezug auf Effizienz oder Eleganz bieten?

Vielen Dank!

+0

Beachten Sie, dass aus Gründen der Effizienz willen, 'a contains' könnte mit einem' contains' auf einer 'toSet' Version des gleichen' 'Seq' a' substituiert sein kann; Dies kann die Leistung bei großen 'Seq's verbessern, ohne diesen Code dramatisch zu verändern. – logtwo

+0

Ist der Eingang garantiert bestellt? –

+0

@TravisBrown In meinem Fall ja, aber wir könnten immer eine sortierte Methode auf Seq a anwenden, bevor wir nach Bereichen suchen.Somit würde ich sagen, dass sortierte Elemente in jedem Fall eine vernünftige Startbedingung ist. – logtwo

Antwort

2

Wenn die Eingabe ist bestellt (oder Sie sind bereit, es zuerst zu sortieren), können Sie dies ziemlich präzise in einem einzigen Durchgang mit foldLeft tun (gut, zwei Pässe mit der reverse, aber das ist ein Artefakt der Arbeit mit Listen und könnte vermieden werden, wenn Sie bereit sind, eine gewisse Eleganz geben):

seq.foldLeft[List[(Int, Int)]](Nil) { 
    case ((a, b) :: rest, i) if i == b + 1 => (a, i) :: rest 
    case (acc, i) => (i, i) :: acc 
}.reverse 

die in diesem Fall uns folgendes ergibt:

res0: List[(Int, Int)] = List((1,6), (9,11), (14,16), (18,18)) 

Für jedes Element, das wir prüfen, ob es der Nachfolger des ist Ende der letzten Reihe wir a dded. Wenn dies der Fall ist, ersetzen wir das Ende in diesem Bereich. Wenn nicht, beginnen wir eine neue Reihe.

+0

Eine sehr gute Lösung, danke @Travis Brown! – logtwo

2

So etwas wie dies vielleicht:

(None +: seq.toStream.map(x => Some(x)) :+ None).sliding(2).flatMap { 
    case Seq(None, Some(b)) => List(b) 
    case Seq(Some(a), None) => List(a) 
    case Seq(Some(a), Some(b)) if b - a == 1 => Nil 
    case Seq(Some(a), Some(b)) => List(a,b) 
}.toList 
+0

Eine clevere Lösung, danke @Dima! – logtwo

+0

Dima, es gibt einen kleinen Fehler im Code: Wenn du 'val seq = List (18,19,21)' hast, ergibt sich 'List (18,19,21) ', was nicht gut ist (es sollte' sein Liste (18,19,21,21) 'stattdessen, da Elementzählung immer gerade sein sollte). Ich denke, dass dies passiert, da die von 'flatMap' verwendete Liste ist, in diesem Fall: Liste (18), Liste (18, 19), Liste (19, 21)), also nach dem Kopieren der Liste (19 , 21) 'in der Zwischenliste (Pre-Flat-Mapping) gibt es keine' 21' (oder 'List (21)'), die kopiert werden sollen. Was denken Sie? – logtwo

+0

@logtwo yeah, du hast Recht. Es hat das letzte Element nicht korrekt behandelt. Es wurde jetzt behoben. Es war nicht genug, ein Dummy-Element vor den Kopf zu stellen (am Ende brauchte ich den gleichen Trick), also habe ich es stattdessen auf "Optionen" gemappt, so dass wir 'None' als Sentinel verwenden können. Beachte, dass du es in einen Stream in der Mitte umwandeln musst - das vermeidet ein zusätzliches Traversal, so dass alles auf einmal erledigt werden kann. – Dima

Verwandte Themen