0

Alternative Title : How to optimize grouping each SObject records by Recordtypes

public static Id sgRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Singapore').getRecordTypeId();
public static Id myRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Malaysia').getRecordTypeId();

for(Account accObj : newAccountList) {
     SWITCH on accObj.RecordTypeId {
        when sgRecordTypeId {
            sgAccountIdSet.add(accObj.Id);
        }
        when myRecordTypeId {
            myAccountIdSet.add(accObj.Id);
        }
...
}

When I do the above code I get this error : Id is not a valid switch expression type

So I tried changing it to SWITCH on String.valueOf(accObj.RecordTypeId) { as mentioned here but get these errors:

1. null occurs as more than one when branch for this switch statement
2. Field must be an enum reference

Realised the error is because You can only use explicit string literals (no variables)

But got stuck here ...

Note : Don't want to use if/else statement because it will make the Apex PMD complain about the cyclomatic complexity

2 Answers 2

2

Switch statements can only work on static (compile-time) values, not any computed value. As such, you might want to work with record type names instead:

Map<Id, RecordType> recordTypes = new Map<Id, RecordType>([
  SELECT Name FROM RecordType WHERE sObjectType = 'Account'
]);
for(Account accRecord: newAccountList) {
  switch on recordTypes.get(accRecord.RecordTypeId)?.Name {
    when 'Singapore' {
      ...
    }
    when 'Malaysia' {
      ...
    }
  }
}

For your specific use case, you can also use a Map by way of references:

public static Id sgRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Singapore').getRecordTypeId();
public static Id myRecordTypeId = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Malaysia').getRecordTypeId();
Set<Id> sgAccountIdSet = new Set<Id>();
Set<Id> myAccountIdSet = new Set<Id>();
Map<Id, Set<Id>> accountIdsByRecordTypeId = new Map<Id, Set<Id>> {
  sgRecordTypeId => sgAccountIdSet,
  myRecordTypeId => myAccountIdSet
};
for(Account record: newAccountList) {
  accountIdsByRecordTypeId.get(record.RecordTypeId)?.add(record.Id);
}

Because objects are accessed by reference, updating the value in the Map will update the related set variables.

2
3

Per my understanding, you want to group account ids by their RecordTypeId. Consider using a Map for this purpose. It reduces sets where you are storing grouped elements:

Map<Id, Set<Id>> accountsIdsByRecordTypeId = new Map<Id, Set<Id>>();

for(Account accObj : newAccountList){
    Set<Id> accountIds = new Set<Id>();
    if(accountsIdsByRecordTypeId.containsKey(accObj.RecordTypeId)){
        accountIds = accountsIdsByRecordTypeId.get(accObj.RecordTypeId);
    }
    accountIds.add(accObj.Id);
    accountsIdsByRecordTypeId.put(accObj.RecordTypeId, accountIds);
}

Key of accountsIdsByRecordTypeId is a RecordTypeId of Account and value is a Set of Account ids.


if you have to group accounts only by specific RecordTypeId, create another Set to store ids to filter:

Map<String, Schema.RecordTypeInfo> recordTypeInfos = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName();

Set<Id> accountRecortypeIdsToGroupBy = new Set<Id> {
    recordTypeInfos.get('Singapore').getRecordTypeId(),
    recordTypeInfos.get('Malaysia').getRecordTypeId();
};

Map<Id, Set<Id>> accountsIdsByRecordTypeId = new Map<Id, Set<Id>>();

for(Account accObj : newAccountList){
    if(accountRecortypeIdsToGroupBy.contains(accObj.RecordTypeId)) {
        Set<Id> accountIds = new Set<Id>();
        if(accountsIdsByRecordTypeId.containsKey(accObj.RecordTypeId)){
            accountIds = accountsIdsByRecordTypeId.get(accObj.RecordTypeId);
        }
        accountIds.add(accObj.Id);
        accountsIdsByRecordTypeId.put(accObj.RecordTypeId, accountIds);
    }
}
8
  • Yes this is a trigger handler that needs to split the accounts triggered by the multiple recordtypes which then needs to be fed to a separate handlers for each country. btw in your second code paragraph you're not calling accountRecortypeIdsToGroupBy ?
    – compski
    Commented Nov 10, 2021 at 12:54
  • @compski there was a typo, please check updated answer if(accountRecortypeIdsToGroupBy.contains(accObj.RecordTypeId)) { Commented Nov 10, 2021 at 12:58
  • 1
    line Set<Id> accountIds = new Set<Id>(); creates empty set with accounts ids belong to one recordtype id, after that it checks if this set is already present in Map if(accountsIdsByRecordTypeId.containsKey(accObj.RecordTypeId) by account record type key, if so previosly createed empty set is assigned with valeus from the Map accountIds = accountsIdsByRecordTypeId.get(accObj.RecordTypeId); Commented Nov 10, 2021 at 13:20
  • 2
    The algorithm in your code is like 2x slower than just pre-populating the map with the possible keys ahead of time: for(Account record: newAccountList) accountIdsByRecordTypeId.put(record.RecordTypeId, new Set<Id>()); for(Account record: newAccountList) accountIdsByRecordTypeId.get(record.RecordTypeId).add(record.Id); You can eliminate all the if statements, saving you a contains and containskey
    – sfdcfox
    Commented Nov 10, 2021 at 14:25
  • 1
    @OleksandrBerehovskyi Yeah, you know I'm usually all about performance. I wrote something up you might want to read here if you're interested in performance.
    – sfdcfox
    Commented Nov 10, 2021 at 15:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .