Reputation: 159844
Martin Odersky gives this example to split out an array into 2 collections:
val people: Array[Person]
val (minors, adults) = people partition (_.age < 18)
class Person(val name: String, val age: Int)
What is the best way to split out into 3 or more arrays:
val (minors, adults, seniors) = people partition?? x < 18, 18 < x < 65, x > 65 // ?
Upvotes: 2
Views: 668
Reputation: 51109
def multiPartition[T: Manifest](xs: Traversable[T])(fs: T => Boolean *) = {
val builders = Vector.fill(fs.length)(ArrayBuffer.empty[T])
for (e <- xs) {
val i = fs.indexWhere(f => f(e))
if (i >= 0) builders(i) += e
}
builders.map(_.toArray)
}
Example usage would be:
val Seq(minors, adults, seniors) =
multiPartition(people)(_.age < 18, _.age < 65, _ => true)
so each term in the second arg list is a "bucket", with _ => true
being "everything else".
This returns Arrays. I made a very similar version that spits out your original collection type here, although I don't know how to get that to work for Arrays as well.
Upvotes: 3
Reputation: 18869
You may use the method groupBy:
case class Person(name: String, age: Int)
val people = Array(Person("A",1),Person("B",2),Person("C",14),Person("D",19),Person("E",70))
def typeByAge(p: Person): String =
if(p.age < 4) "BABY"
else if(p.age < 18) "MINOR"
else if(p.age < 65) "ADULT"
else "OLD"
val g = people groupBy typeByAge
val (babys,minors,adults,olds) = (g("BABY"),g("MINOR"),g("ADULT"),g("OLD"))
Upvotes: 2
Reputation: 20285
This will return a Tuple3 like you note in the question of minors, adults, and seniors. There is probably a more efficient and general approach though.
Tuple3(people.filter(_.age < 18), people.filter(p => p.age > 18 && p.age < 65), people.filter(_.age > 65))
Upvotes: 2
Reputation: 1765
Doing that with a one-liner would be probably less pretty.
val (minors, older) = people partition {_.age < 18}
val (adults, seniors) = older partition {_.age < 65}
Upvotes: 7