2010-10-22 5 views
2

fondamentalement je ne suis pas vraiment un fan de Java/Scala, mais malheureusement, je suis obligé de l'utiliser pour mes études. De toute façon, on m'a assigné une tâche:scala liste d'objets, en utilisant groupBy avec la moyenne

Ce que le programme obtient est une liste d'objets comme: Mark(val name String, val style_mark Int, val other_mark Int).

Comment utiliser groupBy, pour grouper les marques par nom, et obtenir une moyenne pour style_mark et other_mark?

Mark("John", 2, 5) 
Mark("Peter", 3, 7) 
Mark("John", 4, 3) 

doit retourner:

Mark("John", 3, 4) 
Mark("Peter", 3, 7) 

Cest le code:

class Mark(val name: String, val style_mark: Int, val other_mark: Int) {} 

object Test extends Application 
    { 
    val m1 = new Mark("Smith", 18, 16); 
    val m2 = new Mark("Cole", 14, 7); 
    val m3 = new Mark("James", 13, 15); 
    val m4 = new Mark("Jones", 14, 16); 
    val m5 = new Mark("Richardson", 20, 19); 
    val m6 = new Mark("James", 4, 18); 

    val marks = List(m1, m2, m3, m4, m5, m6); 

    def avg(xs: List[Int]) = xs.sum/xs.length 

    marks.groupBy(_.name).map { kv => Mark(kv._1, avg(kv._2.map(_.style_mark)), avg(kv._2.map(_.other_mark))) } 

    println(marks); 
    } 

Toute aide serait grandement appréciée,

Paul

Répondre

3

Comme vous avez déjà dit, nous peut utiliser groupBy pour grouper les marques par leur nom. Maintenant, nous avons un Map où chaque clé est le nom et la valeur est une liste de marques avec ce nom.

Nous pouvons maintenant itérer que Map et remplacer chaque paire clé-valeur avec une marque-objet qui a la clé comme son nom, et la moyenne des style_mark s dans la liste comme style_mark et la moyenne des other_mark s dans la liste en tant que other_mark. Comme ceci:

def avg(xs: List[Int]) = xs.sum/xs.length 
marks.groupBy(_.name).map { kv => 
    Mark(kv._1, avg(kv._2.map(_.style_mark)), avg(kv._2.map(_.other_mark))) 
} 
+0

Merci pour la réponse, je l'ai en quelque sorte compris. Mis à jour ma question avec le code que je connais bien, mais il me donne une erreur 'error: non trouvé: value Mark' at' Mark (kv._1, ... ' – PawelMysior

+0

@Pawel: Si' Mark' n'est pas un classe de cas, vous devez écrire 'new Mark (bla, bla, bla)' au lieu de 'Mark (bla, bla, bla)'. – sepp2k

+0

@Pawel: Notez également que la valeur de 'marques' restera inchangée, de sorte que vous devrait imprimer la valeur de retour de l'expression, pas la valeur de 'marks'. (Bien qu'il soit plus facile de tester le code dans REPL qu'en créant un fichier et en l'exécutant, vous pouvez simplement entrer l'expression et voir son résultat – sepp2k

6

Juste quelques points ici:

  1. Vous pouvez utiliser la correspondance de modèles pour éviter ce genre de choses fastidieuses _1, _2 qui vient avec tuples.

  2. dans les noms variables Insiste/paramètres sont une mauvaise chose ™, ils sont déjà utilisés beaucoup trop fortement ailleurs dans la langue

Donc, après avoir déclaré que:

MISE À JOUR: remplacé avg avec avgOf, ce qui réduit la duplication :)

//Needs two param lists so that inference will work properly 
//when supplying the closure 
def avgOf[T](xs:List[T])(f:(T)=>Int) = xs.map(f).sum/xs.length 

marks.groupBy(_.name).map { 
    case (k,v) => new Mark(k, avgOf(v)(_.styleMark), avgOf(v)(_.otherMark)) 
} 

Dans la rea Dans le monde entier, j'aurais probablement proxénété Traversable pour ajouter la méthode avgOf, donc vous pourriez écrire v.avgOf(_.styleMark), mais cela ne ferait que compliquer cet exemple.

+1

Il devrait y avoir 'case' avant' (k, v) '. – missingfaktor

+0

Ce ne serait pas la première fois que j'ai fait cette erreur non plus :) –