I trying to learn Triggers from TrailHead, but not able to understand this line of code.

Map<Id,Account> acctsWithOpps = new Map<Id,Account>(
    [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.New]);

This is a unique Apex idiom. The Map class has a constructor that accepts a list of sObjects, such as the literal result of a SOQL query. The purpose of the constructor is to efficiently populate a Map between the Ids of the sObjects and the sObjects themselves.

This is useful in many places, but in triggers in particular it's often used when you're querying and using related objects. You'll typically need fast access to specific related objects based on their Ids stored in the records your trigger is processing. Using a Map allows you to index to those records directly rather than iterating through a list, which is very inefficient.

That pattern often looks like this:

Set<Id> myIdSet = new Set<Id>();
for (Contact c : Trigger.new) {

Map<Id, Account> parentAccounts = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :myIdSet]);

for (Contact c : Trigger.new) {
    Account accountForThisContact = parentAccounts.get(c.AccountId);

The critical bit is the line

Account accountForThisContact = parentAccounts.get(c.AccountId);

This allows us to immediately access queried Accounts (or any other sObject) by just its Id, with no iteration. Technically speaking, accessing a Map is an O(1) operation, which means it executes in constant time regardless of the number of records in the Map.

  • "unique" isn't exactly right. Maps exist in almost all modern languages.
    – sfdcfox
    Nov 7 '18 at 14:28
  • 1
    I was referring to the specific usage new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :myIdSet]), i.e., passing SOQL results directly into the Map constructor.
    – David Reed
    Nov 7 '18 at 14:30

SOQL query returns List of Sobjects. In current case it returns List of Accounts.

Map class have constructor, that accepts List of Sobjects. Map<ID,sObject>(recordList). This constructor:

Creates a new instance of the Map class and populates it with the passed-in list of sObject records. The keys are populated with the sObject IDs and the values are the sObjects.

as a result of it, in your Map keys are ids of queried Account and values are queried Accounts. The following code snippet illustrates this principle. Try it.

List<Account> accountsList = [
    select Id, Name
    from Account
Map<Id, Account> accountsMap = new Map<Id, Account>(accountsList);
for(Id accountId :accountsMap.keySet()){
    System.debug('Key:' + accountId + ', value:' + accountsMap.get(accountId));

Records are related to each other using Id values i.e. one record references another record by having a (foreign key) field that contains the Id of the other object. Triggers need to be written to handle the "bulk" case - many records at once - because the Salesforce platform puts limits on many operations such as making queries. So it is quite common that while looping over one list of SObjects, you want to find the data for the referenced object. That is where maps come in.

For example:

for (Contact c : Trigger.new) {
    Account a = acctsWithOpps.get(c.AccountId);

where here AccountId is the foreign key field.

If maps were not used, the code would be more complicated and finding the matching object would be much more costly because the list would need to be looped over to find the match. A map divides up the data added so that a value can be quickly found largely independently of the number of records.


A SOQL query will always return a list of sobjects (only exception will be if using Aggregate queries then it can return an Integer or AggregatQueryResult)

Now There is a constructor for Map that accepts a List, and automatically makes the Sobject Id as its Key.

So the List of SObject can be passed to constuctor, So the single Line code can be written in few lines for Simplification like

List<Account> accList = [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.New];
Map<Id,Account> accountMap = new Map<Id,Account>(accList );

Also you can also get the list back from MAP using

List<Account> accList = accountMap.values();

Src: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_map.htm

  • "A SOQL query will always return a list of sobjects." Well it is not true actually. It can return also single SObject and Integer Nov 7 '18 at 14:34
  • @user1974566 I agree with aggregate function exception, but it always returns a list. List with 1 element is still a list. Though you can assign it to an individual record it will always be a list. Nov 7 '18 at 14:37
  • If you select a record with specified id you'll recieve sobject and not list<sobject> with size() = 1 Nov 7 '18 at 18:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.