Q:

Single iteration => Multiple output collections from Java to Scala

I'm currently trying to convert some Java code to Scala code. The challenge is to ensure that the converted Scala code doesn't end up doing something very inefficient when compared to the original Java one. For e.g. when trying to convert the following code:
class Person {
    String name;
    Integer age;
    Character gender;
}

public class TestJava {
    public static void main(String[] args) {
        final List<Person> persons = new ArrayList<>();
        final List<Person> males = new ArrayList<>();
        final List<Person> aNames = new ArrayList<>();
        final List<Person> seniors = new ArrayList<>();
        for (final Person p: persons) {
            if (p.gender == 'm') {
                males.add(p);
            }
            if (p.age >= 60) {
                seniors.add(p);                
            }
            if (p.name.startsWith("a")) {
                aNames.add(p);
            }            
        }
    }
}

Since Java relies on mutation, this code looks logical. But, now I want to convert this to its Scala equivalent without looping over the collection multiple times (3 times in this case).
I can of course use mutable List from the Scala library and achieve the same thing as done by Java but was wondering if it was possible to generate multiple collections from a given sequence/collection in a functional/Scala way without iterating over the collection for n times where n is the criteria count. Thanks in advance!

A:

One purely functional and immutable way would be to have a generic function that separates collections into buckets, by predicate:
case class Person(name: String, age: Int, gender: String)

def bucketsByPredicate(people: Seq[Person], predicates: Seq[Person => Boolean]) = {
  people.foldLeft(predicates.map(predicate =>
    (predicate, List.empty[Person])
  )) { case (predicates, person) =>
      predicates.map { case (predicate, members) =>
        (predicate, if(predicate(person)) person :: members else members)
      }
  }.map(_._2)
}

Then an example usage could be:
val olderThan60 = (p: Person) => p.age >= 60
val male = (p: Person) => p.gender == "m"
val Seq(olderThan60People, malePeople) = bucketsByPredicate(people, Seq(olderThan60, male))

A:

case class Person(gender:Sex,name:String,age:Int)

sealed trait Sex
case object Male extends Sex  
case object Female extends Sex

def part3(l:List[Person]) = {
  def aux(l:List[Person],acc:(List[Person],List[Person],List[Person])) : (List[Person],List[Person],List[Person]) = l match {
    case Nil => acc
    case head::tail => {
      val newAcc = (if (head.gender == Male) head :: acc._1 else acc._1,
                    if (head.age >= 60) head :: acc._2 else acc._2,
                    if (head.name startsWith "a") head :: acc._3 else acc._3)
      aux(tail,newAcc)
    }
  }
  val emptyTuple = (List[Person](),List[Person](),List[Person]())
  // It is (much) faster to reverse the input list and then prepend during to recursive loop.
  // prepend is O(1), append is O(n)
  aux(l.reverse,emptyTuple)
}

val (males,seniors,aNames) = part3(List(Person(Male,"abc",20),Person(Female,"def",61),Person(Male,"Nope",99)))
println(s"males : $males \nseniors : $seniors \naNames : $aNames")

// outputs
// males : List(Person(Male,abc,20), Person(Male,Nope,99))
// seniors : List(Person(Female,def,61), Person(Male,Nope,99))
// aNames : List(Person(Male,abc,20))

(The List[Person] tuple makes this look quite ugly, you may want to define a type alias for your results.)

A:

This is fairly pragmatic. Pattern match over the list of persons, check the conditions in each recursive call and add to the results as necessary until the list is exhausted. The results are a List of Person that gets stuffed into a Tuple.
class Person(val name: String, val age: Integer, val gender: Character){
  override def toString = name + " " + age + " " + gender
}

val alice = new Person("Alice", 18, 'f')
val bob = new Person("Bob", 18, 'm')
val charlie = new Person("Charlie", 60, 'm')
val diane = new Person("Diane", 65, 'f')
val fred = new Person("Fred", 65, 'm')

def filterPersons(persons: List[Person]) = {
  import scala.collection.mutable.{ListBuffer => LB}
  def filterPersonsR(persons: List[Person], males: LB[Person], anames: LB[Person], seniors: LB[Person]): Tuple3[LB[Person], LB[Person], LB[Person]] = persons match {
    case Nil => (males, anames, seniors)
    case h :: t => {
      filterPersonsR(t, if(h.gender == 'm') males += h else males, if(h.name.startsWith("A"))  anames += h else anames, if(h.age >= 60) seniors += h else seniors)
    }
  }
  filterPersonsR(persons, LB(), LB(), LB())
}

Testing...
scala> filterPersons(List(alice, bob, charlie, diane, fred))
res25: (scala.collection.mutable.ListBuffer[Person], scala.collection.mutable.ListBuffer[Person], scala.collection.mutable.ListBuffer[Person]) = (ListBuffer(Bob 18 m, Charlie 60 m, Fred 65 m),ListBuffer(Alice 18 f),ListBuffer(Charlie 60 m, Diane 65 f, Fred 65 m))

scala> res25._1
res26: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Bob 18 m, Charlie 60 m, Fred 65 m)

scala> res25._2
res27: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Alice 18 f)

scala> res25._3
res28: scala.collection.mutable.ListBuffer[Person] = ListBuffer(Charlie 60 m, Diane 65 f, Fred 65 m)

