There are many reasons to use Maps in Apex triggers. Sometimes I want to make a List of Contacts, but I want to pull each one by its ID. This is a good reason to abandon the List and to make a Map<Id, Contact>
. (Some will prefer to use Map<String, Contact>
, and that is okay too.) We could use a for
loop, but let’s try something more efficient.
This is how I used to populate a Map<Id, Contact>
of all Contacts at Accounts in a trigger:
Map<Id, Contact> cs = new Map<Id, Contact>();
for (Contact c : [SELECT id FROM Contact WHERE AccountId IN :trigger.newMap.keyset()]){
cs.put(c.id, c);
}
This takes time and uses script statements, and in a big org/app, we want to minimize both.
Here’s my new way:
Map<Id, Contact> cs = new Map<Id, Contact>(
[select Id FROM Contact WHERE AccountId IN :trigger.newMap.keyset()]
);
It’s pretty simple, and it works well. It also keeps code clean.
More astute readers will note that this is similar to the Trigger context variables Trigger.oldMap
& Trigger.newMap
, which populate a map of the trigger records keyed to their record ids.
Ryan says
Good catch. One thing to note that takes this one step further: the constructor for Map can always take a List, which is essentially what you are using in your new way.
So this can be used anywhere. It can be especially useful when using dynamic soql and the Database.query() method, which returns a List, but when you want a map, like this:
String query = ‘select Id, Name from Account’;
Map accountsMap = new Map((List)Database.query(query));
…(note that Database.query returns a List always, so it can be casted into the strongly-typed list of the type you want, which is what is happening here)
It can also be especially useful when you need to make a map out of a child collection of an SObject, such as the “Contacts” collection of an Account record. For example, when you query an account with all of its contacts …
Account a = [select Id, Name, (select Id, Name from Contacts) from Account limit 1];
… since the “Contacts” collection is just a List, you can make a Map like this:
Map contactsMap = new Map(a.Contacts);
Brian Kwong says
Does this assumes that the map you want is formatted as map with key being the ID of the sobject?
IF I want a map where the id is a lookup value in the sobject, or a map with the string a text/number value in the sobject I would still need to create a loop. Am I correct with this assumption?
David Schach says
Exactly, Brian. This puts the sfdcID as the key, and the sObject as the value. (A map is basically a list of key-value pairs where the keys are unique within the keyset.)
Using any other field as the key requires a loop.
Brian Kwong says
Thanks David – although I was hoping I was wrong with my assuming and found a more awesome way to creating my maps!
Mariappan says
Hi.
Its great to see the map concept without loops.
My question is like : Is it possible to have map =[SOQL Query];
I know its a kind of mad question but if it exist , we could reduce the loops as you did in this blog .
Anyway thanks a lot in advance .
David Schach says
That is close to what I did, but we have to differentiate between a Map and a List. There is a limitation (feature?) in Apex, as in Java or C++, where one must instantiate a new map before populating it.
Apex does let you create a List without instantiating it, and that is what one would get from your query:
List mylist = [SELECT Id, Name FROM Account];
Bottom line: Maps must be instantiated with a “new” call. Lists are what one gets when not instantiating a Map. I hope this helps.