2010-01-26 4 views
3

Je suis venu avec cette mise en œuvre de groupBy:Autre façon d'implémenter une méthode groupBy dans Scala?

object Whatever 
{ 
    def groupBy[T](in:Seq[T],p:T=>Boolean) : Map[Boolean,List[T]] = { 
     var result = Map[Boolean,List[T]]() 
     in.foreach(i => { 
      val res = p(i) 
      var existing = List[T]() // how else could I declare the reference here? If I write var existing = null I get a compile-time error. 
      if(result.contains(res)) 
       existing = result(res) 
      else { 
       existing = List[T]() 
      } 
      existing ::= i 
      result += res -> existing 
     }) 
     return result 
    } 
} 

mais il ne semble pas très Scalish (est-ce le mot que je cherche?) Pour moi. Pourriez-vous suggérer des améliorations?

EDIT: après avoir reçu le « conseil » au sujet de pliage, je l'ai mis en œuvre ainsi:

def groupFold[T](in:Seq[T],p:T=>Boolean):Map[Boolean,List[T]] = { 
     in.foldLeft(Map[Boolean,List[T]]()) ((m,e) => { 
      val res = p(e) 
      m(res) = e :: m.getOrElse(res,Nil) 
     }) 
} 

Que pensez-vous?

+0

La mise en œuvre pli est particulièrement utile lorsque vous avez plus de valeurs que Boolean a. Notez que vous pourriez remplacer Boolean par un type générique U et le fold fonctionnerait toujours! –

Répondre

4

Si vous voulez groupe par un prédicat (c.-à-fonction de T => Boolean), alors vous voudrez probablement juste faire ceci:

in partition p 

Si vous voulez vraiment créer une carte de il, alors:

val (t, f) = in partition p 
Map(true -> t, false -> f) 

Puis encore, vous pouvez juste vouloir l'exercice. Dans ce cas, la solution de pliage est bonne.

+0

Très cool! Je ne savais pas qu'un builtin existait. Merci Daniel! – Geo

+0

@Geo Vous pouvez également regarder 'splitAt' et' span', qui font quelque chose de similaire mais avec des critères différents. Le premier se divise entre 'take' et' drop', tandis que le second se divise entre 'takeWhile' et' dropWhile'. –

1

Petit indice: utilisez folds pour calculer la liste résultante de manière fonctionnelle/immuable.

2

Je voudrais juste filtrer deux fois.

object Whatever { 
    def groupBy[T](in: Seq[T], p: T => Boolean) : Map[Boolean,List[T]] = { 
    Map(false -> in.filter(!p(_)).toList , true -> in.filter(p(_)).toList) 
    } 
} 
+0

Très belle implémentation! – Geo

4

Voici un exemple utilisant foldLeft.

scala> def group[T, U](in: Iterable[T], f: T => U) = { 
    | in.foldLeft(Map.empty[U, List[T]]) { 
    |  (map, t) => 
    |  val groupByVal = f(t) 
    |  map.updated(groupByVal, t :: map.getOrElse(groupByVal, List.empty)) 
    | }.mapValues(_.reverse) 
    | } 
group: [T,U](in: Iterable[T],f: (T) => U)java.lang.Object with scala.collection.DefaultMap[U,List[T]] 

scala> val ls = List(1, 2, 3, 4, 5) 
ls: List[Int] = List(1, 2, 3, 4, 5) 

scala> println(group(ls, (_: Int) % 2)) 
Map(1 -> List(1, 3, 5), 0 -> List(2, 4)) 

Scala 2.8 offre ce dans la bibliothèque standard:

scala> println(ls.groupBy((_: Int) % 2)) // Built into Scala 2.8. 
Map(1 -> List(1, 3, 5), 0 -> List(2, 4)) 
Questions connexes