2016-04-18 3 views
3

У меня есть список:Самый быстрый способ сократить список объектов

case class Person(name:String, salary:Int, cars:Int, country:String) 
    val a = Person("gin", 100, 2, "Ind") 
    val b = Person("gin", 200, 1, "Ind") 
    val c = Person("gin", 50, 1, "US") 
    val d = Person("bin", 10, 0, "US") 
    val e = Person("bin", 20, 2, "UK") 
    val f = Person("bin", 30, 5, "Ind") 
    val list = List(a, b, c, d, e, f) 

Я хотел бы уменьшить приведенный выше список на основе имени и страны, поэтому выход будет

Person("gin", 300, 3, "Ind") 
Person("gin", 50, 1, "US") 
Person("bin", 10, 0, "US") 
Person("bin", 20, 2, "UK") 
Person("bin", 30, 5, "Ind") 

Мое решение это:

listBuffer.groupBy(p => p.name -> p.country).map { 
    case (key, persons) => key -> (persons.map(_.salary).sum, persons.map(_.cars).sum) 
}.map { 
    case ((name, coutry), (ss, cs)) => Person(name, ss, cs, coutry) 
} 

Есть ли более эффективное решение вышеуказанной проблемы?

+1

Вы можете получить человек за один раз без двойной карты (..) на сгруппированных коллекции – Nyavro

+0

Можете ли вы показать мне, как? Спасибо – binshi

ответ

3

В дополнение к предложению Nyavro, вы можете (в порядке убывания уровня абстракции, и, таким образом, как повышение эффективности и снижение компонуемости):

  1. Избегайте промежуточных коллекций в persons.map(...).sum с помощью persons.view.map(...).sum или более непосредственно

    def sumBy[A, B](xs: Seq[A])(f: A => B)(implicit n: Numeric[B]) = 
        xs.foldLeft(n.zero) { (a, b) => n.plus(a, f(b)) } 
    
    sumBy(persons)(_.salary) 
    
  2. в этом случае вы даже можете сделать все за один проход:

    listBuffer.foldLeft(Map.empty[(String, String), Person]) { 
        (map, p) => 
        val key = (p.name, p.country) 
        map.updated(key, (map.get(key) match { 
         case None => p 
         case Some(oldP) => 
         Person(p.name, p.salary + oldP.salary, p.cars + oldP.cars, p.country) 
        }) 
    } 
    
  3. Преобразование вышеуказанного в цикл while (действительно не рекомендуется, если вам действительно не нужна производительность).

+0

Первый вариант выглядит отлично ИМО. –

3

Вы можете сохранить несколько итераций, как это:

list 
    .groupBy(person => person.name->person.country) 
    // Collect Persons in one go: 
    .map { 
    case ((name,cuntry), persons) => 
     // Collect total salary and cars in one go: 
     val (ts, tc) = persons.foldLeft ((0,0)) { 
     case ((salary,cars), item) => (salary+item.salary,cars+item.cars) 
     } 
     Person(name, ts, tc, cuntry) 
} 
Смежные вопросы