Max
Max

Reputation: 2552

How to reduce() on a collection keeping the collection itself in Scala?

I just need to reduce the elements in a collection but I'd like to keep the collection in the result.

scala> List("a","b","c").reduce(_+_)
res0: String = abc

I'd like to get

scala> List("a","b","c").someSortOfReduce(_+_)
res0: List[String] = List(abc)

scala> Seq("a","b","c").someSortOfReduce(_+_)
res1: Seq[String] = Seq(abc)

Upvotes: 2

Views: 218

Answers (4)

LRLucena
LRLucena

Reputation: 1705

Use the companion method

def red[T](a: Iterable[T], f: (T,T) => T) = a.companion(a.reduce(f))

EDITED

If you need to preserve the static type an option is to use asInstanceOf

implicit class Reduce[T, C[T] <: Iterable[T]](s: C[T]) {
  def someSortOfReduce(f: (T, T) => T): C[T] = s.companion(s.reduce(f)).asInstanceOf[C[T]]
}
val list = List(1, 2, 3).someSortOfReduce(_ + _)  // list: List[Int] = List(6)
val set = Set(1, 2, 3).someSortOfReduce(_ + _)    // set: Set[Int] = Set(6)

or Pattern Matching

implicit class Reduce[T, C[T] <: Iterable[T]](s: C[T]) {
  def someSortOfReduce(f: (T, T) => T): C[T] = s.companion(s.reduce(f)) match {
    case a: C[T] => a
  }
}

Upvotes: 2

R&#233;gis Jean-Gilles
R&#233;gis Jean-Gilles

Reputation: 32719

Here is one way to do it (maybe not the most elegant, but hey, it works):

import collection.generic._
def reduceAsCollection[E,C[_]](s: C[E])(f: (E,E) => E)(implicit cbf: CanBuildFrom[Nothing, E, C[E]], e: C[E] <:< TraversableOnce[E]): C[E] = {
  (cbf() += s.reduce(f)).result
}

And the obligatory REPL test:

scala> reduceAsCollection(List("a","b","c"))(_+_)
res14: List[String] = List(abc)

scala> reduceAsCollection(Seq("a","b","c"))(_+_)
res15: Seq[String] = Vector(abc)

As you can see, not only has the resulting collection an appropriate runtime type, but the static type is also preserved (give a Seq get a Seq back, give a List, get a List back).

Upvotes: 3

Max
Max

Reputation: 2552

I found the solution myself

val list = List("a","b","c")
list.companion(list.reduce(_+_))

Upvotes: 1

Kraylog
Kraylog

Reputation: 7553

Since the collection you want and the original collection don't share any entries, you could just wrap the result of the reduce in whatever you want.

Seq(original.reduce(_+_))

Upvotes: 0

Related Questions