[SalesForce] Is it possible to convert Map> to Map> without a loop

As a Salesforce developer, I am frequently working with Maps and am looking to abstract the process of generating maps from SObject lists.

I've created the following helper class:

private class MapBySharedKeyExtractor {
    SObjectField keyField;
    XAP_PRED_SObjectPredicateIntf predicate;

    MapBySharedKeyExtractor(SObjectField keyField, XAP_PRED_SObjectPredicateIntf predicate) {
        this.keyField = keyField;
        this.predicate = predicate;
    }

    public Map<Object, List<SObject>> extractFrom(List<SObject> homogeneousSObjectList) {
        if (homogeneousSObjectList == null || homogeneousSObjectList.isEmpty()) {
            return null;
        }

        Map<Object, List<SObject>> sObjectListByKeyMap = new Map<Object, List<SObject>>();
        for (SObject sObj : homogeneousSObjectList) {
            if (this.predicate.isTrueFor(sObj)) {
                Object key = sObj.get(this.keyField);
                if (!sObjectListByKeyMap.containsKey(key)) {
                    sObjectListByKeyMap.put(key, new List<SObject>());
                }
                sObjectListByKeyMap.get(key).add(sObj);
            }
        }
        return sObjectListByKeyMap;
    }
}

This is the prediate interface:

public interface XAP_PRED_SObjectPredicateIntf {
    Boolean isTrueFor(SObject sObj);
}

However, as the final step of this journey, I would like to be able to change the generated Map into concrete types, so I would like (for example) to have a helper method:

  public static Map<Id, List<SObject>> mapBySpecifiedIdField(List<SObject> homogeneousSObjectList, SObjectField idField) {
        Map<Object, List<SObject>> sObjectListBySpecifiedObjectMap
                = new MapBySharedKeyExtractor(idField, new XAP_PRED_SObjectFieldHasNonNullValue(idField))
                .extractFrom(homogeneousSObjectList);

        String mapType = 'Map<Id, List<' + homogeneousSObjectList[0].getSObjectType() + '>>';
        Map<Id, List<SObject>> concreteSObjectListByIdMap = (Map<Id, List<SObject>>) Type.forName(mapType).newInstance();
        concreteSObjectListByIdMap.putAll(sObjectListBySpecifiedObjectMap);
        return concreteSObjectListByIdMap;
    }

However this fails because this is invalid:

 concreteSObjectListByIdMap.putAll(sObjectListBySpecifiedObjectMap);

Is there any way I can make this work without iterating through sObjectListBySpecifiedObjectMap and casting each key individually?

Best Answer

No. You can't convert key types without a loop. The Map structure is not flexible in this way. The List type is a bit more flexible, however, and so you can make this minor simplification, as concretely typing the List<SObject> offers few tangible benefits unless you plan to upsert it.

Map<Id, List<SObject>> concreteSObjectListByIdMap = new Map<Id, List<SObject>>();

No need for dynamic Type.forName instantiation.

Related Topic