I'm learning Scala as it fits my needs well but I am finding it hard to structure code elegantly. I'm in a situation where I have a List
x
and want to create two List
s: one containing all the elements of SomeClass
and one containing all the elements that aren't of SomeClass
.
val a = x collect {case y:SomeClass => y}
val b = x filterNot {_.isInstanceOf[SomeClass]}
Right now my code looks like that. However, it's not very efficient as it iterates x
twice and the code somehow seems a bit hackish. Is there a better (more elegant) way of doing things?
It can be assumed that SomeClass
has no subclasses.
Use
list.partition
:EDIT
For partitioning by type, use this method:
Usage:
Starting
Scala 2.13
, most collections are now provided with apartitionWith
method which partitions elements based on a function which returns eitherRight
orLeft
.That allows us to pattern match a given type (here
Person
) that we transform as aRight
in order to place it in theright
List of the resulting partition tuple. And other types can be transformed asLeft
s to be partitioned in the left part:given:
Just wanted to expand on mkneissl's answer with a "more generic" version that should work on many different collections in the library:
Just one note: The partition method is similar, but we need to capture a few types:
X -> The original type for items in the collection.
A -> The type of items in the left partition
B -> The type of items in the right partition
CC -> The "specific" type of the collection (Vector, List, Seq etc.) This must be higher-kinded. We could probably work around some type-inference issues (see Adrian's response here: http://suereth.blogspot.com/2010/06/preserving-types-and-differing-subclass.html ), but I was feeling lazy ;)
To -> The complete type of collection on the left hand side
To2 -> The complete type of the collection on the right hand side
Finally, the funny "CanBuildFrom" implicit paramters are what allow us to construct specific types, like List or Vector, generically. They are built into to all the core library collections.
Ironically, the entire reason for the CanBuildFrom magic is to handle BitSets correctly. Because I require CC to be higher kinded, we get this fun error message when using partition:
I'm leaving this open for someone to fix if needed! I'll see if I can give you a solution that works with BitSet after some more play.
EDITED
While using plain
partition
is possible, it loses the type information retained bycollect
in the question.One could define a variant of the
partition
method that accepts a function returning a value of one of two types usingEither
:Then the types are retained:
Of course the solution could be improved using builders and all the improved collection stuff :) .
For completeness my old answer using plain
partition
:For example:
If the list only contains subclasses of
AnyRef
, becaus of the methodgetClass
. You can do this: