2013-03-20 7 views
5

ich oft brauchen so etwas wieEffiziente groupwise Aggregation auf Scala Sammlungen

coll.groupBy(f(_)).mapValues(_.foldLeft(x)(g(_,_))) 

zu tun Was ist der beste Weg, um die gleiche Wirkung zu erzielen, aber vermeiden Sie explizit die Zwischen Sammlungen mit groupBy konstruieren?

+2

@sschaef Können Sie den Grund für die Änderung "Was ist der beste Weg" zu "Ist es möglich und wenn ja wie?" Erklären? Es muss möglich sein (Turing completness) und es ist leicht einen klugen Weg zu finden. Ich mache auch die Frage unggrammatical –

+0

"Was ist der beste Weg" ist schlechtes Format für eine Frage, normalerweise kann es nicht definitiv beantwortet werden. Aber ich stimme dem Rollback zu, nachdem ich über den Schnitt nachgedacht habe, es hat die Frage nicht besser gemacht. – sschaef

Antwort

4

Sie könnten die erste Sammlung über eine Karte hält Ihre Zwischenergebnisse falten:

def groupFold[A,B,X](as: Iterable[A], f: A => B, init: X, g: (X,A) => X): Map[B,X] = 
    as.foldLeft(Map[B,X]().withDefaultValue(init)){ 
    case (m,a) => { 
     val key = f(a) 
     m.updated(key, g(m(key),a)) 
    } 
    } 

Du hast gesagt, Sammlung und ich schrieb Iterable, aber Sie haben in der Falte in Ihrer Frage, ob, um Angelegenheiten zu denken.

Wenn Sie effizienten Code wünschen, verwenden Sie wahrscheinlich eine veränderliche Map wie in Rex 'Antwort.

+0

Wenn ich mich nicht irre, könnte man 'm: + m.get (f (a)). Map (g (_, a)). GetOrElse (g (init, a))' zu 'm: + m .getOrElse (f (a), init) .map (g (_, a)) ' –

+0

@ johnsullivan Danke, das ist schöner. – ziggystar

+0

Beachten Sie, dass obwohl dies Speicher spart, ist es tatsächlich langsamer als das Original. –

3

Sie können es nicht wirklich als Einzeiler machen, also sollten Sie sicher sein, dass Sie es brauchen, bevor Sie etwas Komplizierteres schreiben (geschrieben von einem etwas performanceorientierten Blick, da Sie nach "effizient" gefragt haben): Hier ist ein Beispiel davon in Aktion

final case class Var[A](var value: A) { } 
def multifold[A,B,C](xs: Traversable[A])(f: A => B)(zero: C)(g: (C,A) => C) = { 
    import scala.collection.JavaConverters._ 
    val m = new java.util.HashMap[B, Var[C]] 
    xs.foreach{ x => 
    val v = { 
     val fx = f(x) 
     val op = m.get(fx) 
     if (op != null) op 
     else { val nv = Var(zero); m.put(fx, nv); nv } 
    } 
    v.value = g(v.value, x) 
    } 
    m.asScala.mapValues(_.value) 
} 

(auf Ihrem Anwendungsfall Je Sie in eine unveränderliche Karte statt im letzten Schritt packen möchten.):

scala> multifold(List("salmon","herring","haddock"))(_(0))(0)(_ + _.length) 
res1: scala.collection.mutable.HashMap[Char,Int] = Map(h -> 14, s -> 6)   

Jetzt könnten Sie etwas bemerken komisch hier: Ich benutze eine Java HashMap. Dies liegt daran, dass die HashMaps von Java 2-3x schneller sind als die von Scala. (Sie können das Äquivalent mit einer Scala HashMap schreiben, aber es macht die Dinge nicht schneller als Ihr Original.) Folglich ist dieser Vorgang 2-3x schneller als was Sie gepostet haben. Aber solange du nicht unter schwerem Erinnerungsdruck stehst, tut dir die Erstellung der flüchtigen Sammlungen nicht wirklich weh.

+0

Danke! Mein Hauptproblem ist das Gedächtnis. Ich beschäftige mich mit sehr großen Sammlungen. Für die Iinput-Sammlungen kann ich eine Art von faulen oder out-of-core-Implementierung verwenden, aber das hilft nicht wirklich mit den dazwischenliegenden. –

+0

Wenn Sie sich Gedanken über Speicher machen, können Sie in die Bibliothek trove java collections schauen, die spezielle Primitivsammlungen bietet. – nnythm

+0

@Rex Kerr ist der 3-fache Unterschied in Hashmap-Implementierungen beim Einfügen, Retrieval oder beides? –

Verwandte Themen