Trick: How to obtain a map indexed by any field (not just ID) faster and without loops!

Often we query an object and need to create a map out of the list of records retrieved. The most straightforward way is to declare a Map<ID, sObject> and initialize it with a list resulting from a SOQL query.

List<Account> acctList = [ SELECT ID, Name, AccountNumber
            FROM Account ];

Map<ID, Account> acctMapByID = new Map<ID, Account>( acctList );

The code shown above will populate the acctMapByID with accounts using their IDs as the key.

However, what if we wanted to create a map indexed by AccountNumber instead? We would have to loop through the list and add the records to the map one by one specifying theAccountNumber as the key.

List<Account> acctList = [ SELECT ID, Name, AccountNumber
            FROM Account ];

Map<String, Account> acctMapByNumber =
            new Map<String, Account>();

for( Account anAccount : acctList ) {
    acctMapByNumber.put( anAccount.AccountNumber, anAccount );
}

Now what if there was a way to do that without any loops and in a single line of code?

Map<String, SObject> acctMapByNumber =
       MapCreator.createMapByIndex( acctList, 'AccountNumber' );

Below is the definition of the MapCreator class that allows that.

public class MapCreator {
    public static Map<String, SObject> createMapByIndex(
        List<SObject> aList, String indexField ) {

    // get the list in JSON format
    String jsonList = JSON.serialize( aList );

    // remove enclosing []
    jsonList = jsonList.substring( 1, jsonList.length() - 1 );

    // copy the indexField value in front of each
    // {} group using RegEx substitution
    // example result: value:{..."indexField":"value"...}
    jsonList = '{' + jsonList.replaceAll(
        '(\\{.*?"' + indexField + '":"(.*?)"(,".*?")*\\},?)'
        , '"$2":$1' ) + '}';

    // create map from the modified JSON
    Map<String, SObject> changedMap =
        (Map<String, SObject>) JSON.deserialize(
            jsonList, Map<String, SObject>.class );

    return changedMap;
    }

}

It first converts the list to a JSON string, then modifies the string to look as if it was a serialized Map. It does that by using RegEx to insert the index in front of each record in the string. It then deserializes the modified JSON string into a map.
It doesn’t use any loops explicitly although there must be loops hidden in the RegEx and JSON method internal implementations, which are much faster.

What would be the advantages of using this class instead of coding the loop?

The MapCreator class uses string replacement/RegEx, which is faster than looping and creating map elements one by one.

According to my tests, it runs faster than looping for lists of roughly 8 records or more. For 63 records, it should be 5 to 6 times faster depending on the size of the records.

It might not be relevant for most of the situations but if you have a long running code that needs to be optimized, it might be worth a try.

PS.: I fixed a problem with the way WordPress displays code that made it convert quotes and double-quotes to special characters. Now the code can be copied and pasted onto Salesforce.
 

Advertisements

Posted on 12/29/2014, in No category. Bookmark the permalink. 1 Comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: