I need to intersect types of incoming objects with a set of predefined ones.
The raw approach is to scan an incoming collection for each predefined type:
trait Field
class Field1 extends Field
class Field2 extends Field
class Field3 extends Field
...
class FieldManager(shownFields:Iterable[Field]) {
var hiddenFields = new ArrayBuffer[Field]
var found = false
for (sf <- shownFields) {
if (sf.isInstanceOf[Field1]) {
found = true
break
}
if (!found)
hiddenFields+=new Field1
for (sf <- shownFields) {
if (sf.isInstanceOf[Field2]) {
found = true
break
}
if (!found)
hiddenFields+=new Field2
for (sf <- shownFields) {
if (sf.isInstanceOf[Field3]) {
found = true
break
}
if (!found)
hiddenFields+=new Field3
...
}
Wow, this is so verbose for Scala! There should be a shorter way. Like function template in C++:
class FieldManager {
vector<Field*> hiddenFields, shownFields;
template<class T>
void fillHiddenType() {
FOR_EACH(Field *, field, shownFields) {
if (dynamic_cast<T*>(field))
return
hiddenFields.push_back(new T)
}
}
void fillHidden() {
fillHiddenType<Field1>();
fillHiddenType<Field2>();
fillHiddenType<Field3>();
...
}
};
In this C++ example we mention each type to be scanned for only once.
Ok, Scala is also good for its type parameters, lets try:
def fillHiddenType[T] {
for (sf <- shownFields)
if (sf.isInstanceOf[T])
return
hiddenFields+=new T
}
But compiler don't like creating T instance: class type required but T found
. Try passing instance as argument and the next problem appears warning: abstract type T in type is unchecked since it is eliminated by erasure
and isInstanceOf[]
is always true! Type T is abstarcted so good, that is erased out completly even [T <: Field] doesn't allow to compare it to types from our collection shownFields.
The question is: How to test presence of an object of a given type in a collection in a compact fashion?
Update The following working code was tested on Scala 2.7.7 Both answers were useful. Thank you, guys!
//Scala 2.7.7 misses toSet members
implicit def toSet[T](i:Iterable[T]): Set[T] = {
val rv = new HashSet[T]
rv ++= i
rv
}
def filterOut[T](all:Iterable[T], toRemove:Set[T]) = {
all.filter(x => ! toRemove.contains(x))
}
def filterOutByType[T <: AnyRef](all:Iterable[T], toFilter:Set[T]):Iterable[T] = {
def toClass(x:AnyRef) = x.getClass.asInstanceOf[Class[T]]
val allTypes = all map toClass
val extraTypes = toSet(filterOut(allTypes, toFilter map toClass))
all.filter(extraTypes contains toClass(_))
}