1

How can I reduce a list like below concisely

Seq[Temp] = List(Temp(a,1), Temp(a,2), Temp(b,1))

to

List(Temp(a,2), Temp(b,1))

Only keep Temp objects with unique first param and max of second param.

My solution is with lot of groupBys and reduces which is giving a lengthy answer.

yalkris
  • 2,596
  • 5
  • 31
  • 51

3 Answers3

2

you have to

  • groupBy
  • sortBy values in ASC order
  • get the last one which is the largest

Example,

scala> final case class Temp (a: String, value: Int)
defined class Temp

scala> val data : Seq[Temp] = List(Temp("a",1), Temp("a",2), Temp("b",1))
data: Seq[Temp] = List(Temp(a,1), Temp(a,2), Temp(b,1))

scala> data.groupBy(_.a).map { case (k, group) => group.sortBy(_.value).last }
res0: scala.collection.immutable.Iterable[Temp] = List(Temp(b,1), Temp(a,2))

or instead of sortBy(fn).last you can maxBy(fn)

scala> data.groupBy(_.a).map { case (k, group) => group.maxBy(_.value) }
res1: scala.collection.immutable.Iterable[Temp] = List(Temp(b,1), Temp(a,2))
prayagupa
  • 30,204
  • 14
  • 155
  • 192
1

You can generate a Map with groupBy, compute the max in mapValues and convert it back to the Temp classes as in the following example:

case class Temp(id: String, value: Int)

List(Temp("a", 1), Temp("a", 2), Temp("b", 1)).
  groupBy(_.id).mapValues( _.map(_.value).max ).
  map{ case (k, v) => Temp(k, v) }
// res1: scala.collection.immutable.Iterable[Temp] = List(Temp(b,1), Temp(a,2))

Worth noting that the solution using maxBy in the other answer is more efficient as it minimizes necessary transformations.

Leo C
  • 22,006
  • 3
  • 26
  • 39
0

You can do this using foldLeft:

data.foldLeft(Map[String, Int]().withDefaultValue(0))((map, tmp) => {
    map.updated(tmp.id, max(map(tmp.id), tmp.value))
  }).map{case (i,v) => Temp(i, v)}

This is essentially combining the logic of groupBy with the max operation in a single pass.

Note This may be less efficient because groupBy uses a mutable.Map internally which avoids constantly re-creating a new map. If you care about performance and are prepared to use mutable data, this is another option:

val tmpMap = mutable.Map[String, Int]().withDefaultValue(0)

data.foreach(tmp => tmpMap(tmp.id) = max(tmp.value, tmpMap(tmp.id)))

tmpMap.map{case (i,v) => Temp(i, v)}.toList

Use a ListMap if you need to retain the data order, or sort at the end if you need a particular ordering.

Tim
  • 26,753
  • 2
  • 16
  • 29