I frequently find I want/need to map a list of SObjects by a value other than it's own Id.
So, I'd like to abstract this process to make it reusable.
For example, I might want to map Contacts by Account Id.
So I've created a method mapBySpecifiedIdField:
public static Map<Id, Set<SObject>> mapBySpecifiedIdField (List<SObject> homogeneousSObjectList, SObjectField idField) {
if (homogeneousSObjectList == null || homogeneousSObjectList.isEmpty()) {
return null;
}
Map<Id, Set<SObject>> sObjectSetBySpecifiedIdMap = new Map<Id, Set<SObject>>();
for (SObject sObj : homogeneousSObjectList) {
Id specifiedId = (Id) sObj.get(idField);
if (!String.isBlank(specifiedId)) {
if (!sObjectSetBySpecifiedIdMap.containsKey(sObj.Id)) {
sObjectSetBySpecifiedIdMap.put(sObj.Id, new Set<SObject>());
}
sObjectSetBySpecifiedIdMap.get(sObj.id).add(sObj);
}
}
String mapType = 'Map<Id, Set<' + homogeneousSObjectList[0].getSObjectType() + '>>';
Map<Id, Set<SObject>> concreteSObjectSetByIdMap = (Map<Id, Set<SObject>>)Type.forName(mapType).newInstance();
concreteSObjectSetByIdMap.putAll(sObjectSetBySpecifiedIdMap);
return concreteSObjectSetByIdMap;
}
I'd prefer to invoke it with:
Map<Id, Set<Contact>> mapContactSetByAccountId = (Map<Id, Set<Contact>>) MapHelper.mapBySpecifiedIdField(contactList, Contact.AccountId);
But first, I'll get a compile error:
Incompatible types since an instance of
Map<Id,Set<SObject>>
is never an instance ofMap<Id,Set<Contact>>
If I change my invocation to:
Map<Id, Set<Object>> mapContactSetByAccountId = MapHelper.mapBySpecifiedIdField(contactList, Contact.AccountId);
But this results in a runtime error:
System.TypeException: Invalid conversion from runtime type
Map<Id,Set<Contact>>
toMap<Id,Set<SObject>>
Is there any way I can make this work?
Set<SObject>
as the map value? The problem here lies with trying to upcast a set. If you use aList<SObject>
orMap<Id, SObject>
in its place, your idea should work.