0

Create a custom field called “Number of Locations” (its API name: NumberofLocations__c) on the Account Object (Data Type=Number).Create multiple contacts by Account depending on the NumberofLocations__c field.

Trigger

trigger ContactsCreation on Account (after insert) {
    list<contact> listContact = new list<contact>();
    map<id,decimal> mapAcc=new map<id,decimal>();
    for(Account acc:trigger.new){
        mapAcc.put(acc.id,acc.NumberofLocations__c);
    }
    if(mapAcc.size()>0 && mapAcc!=null){
        for(Id accId:mapAcc.keyset()){
            for(integer i=0;i<mapAcc.get(accId);i++){
                contact newContact=new contact();
                newContact.accountid=accId;
                newContact.lastname='contact'+i;
                listContact.add(newContact);
            }
        }
    }
    if(listContact.size()>0 && listContact!=null)
        insert listContact;
}
3
  • I don't know how you can avoid the for loop inside the first for loop but I think your map is useless. You can use the second for loop in the first loop through Trigger.new. It avoids using the same for loop twice and the use of the Map. Commented Apr 10, 2017 at 12:48
  • @MartinLezer The 2nd for loop can easily be avoided.
    – crmprogdev
    Commented Apr 10, 2017 at 12:53
  • Just a warning - your if statements should be reversed, at the moment if the list is null they will attempt to dereference a null list to call size() before checking whether the list is null, throwing an exception. Commented Apr 10, 2017 at 13:58

2 Answers 2

6

No, you cannot generate a list of child Contact records without a for loop. You can, however, simplify testing and maintenance by breaking out a few of these methods into a Service Layer.

A few notes:

  • You should add error handling to any cross-object DML in your triggers.
  • There's no need to check if the list is empty.
  • It is faster to set all fields at time of SObject construction by passing in name=value pairs.

Service Layer

More atomic build methods:

public static List<Contact> buildLocations(List<Account> records)
{
    List<Contact> locations = new List<Contact>();

    for (Account record : records)
        locations.addAll(buildLocations(record.Id, record.NumberOfLocations__c);
        // this way you only have to unit test one loop at a time
    
    return locations;
}
public static List<Contact> buildLocations(Id accountId, Integer count)
{
    List<Contact> locations = new List<Contact>();
    
    for (Integer i = 0; i < count; i++)
        locations.add(new Contact(AccountId=accountId, LastName='Contact' + i));
        // faster construction
    
    return locations;
}

Error handling:

public static void safeInsertLocations(List<Contact> locations, Map<Id, Account> parents);
{
    if (locations == null) return;
    // this strategy is called a "guard clause"
    // you don't have to guard against an empty list though

    try
    {
        insert locations;
    }
    catch (DmlException d)
    {
        for (Integer i = 0; i < d.getNumDml(); i++)
        {
            Account parent = parents.get(locations[i].AccountId);
            parent.addError(d);
        }
        // this is a pretty standard approach
        // for mapping errors back to parent records
    }
}
0
1

You can iterate on the accId as you've already set it up and just add 1 to an integer variable that you set up at before the beginning of the loop. Change your code as follows:

trigger ContactsCreation on Account (after insert) {
    list<contact> listContact = new list<contact>();
    map<id,decimal> mapAcc=new map<id,decimal>();
    for(Account acc:trigger.new){
        mapAcc.put(acc.id,acc.NumberofLocations__c);
    }
    if(mapAcc.size()>0 && mapAcc!=null){
        integer i = 0;
        for(Id accId:mapAcc.keyset()){                
            contact newContact=new contact();
            newContact.accountid=accId;
            newContact.lastname='contact'+i;
            listContact.add(newContact);
            integer i++;           
        }
    }
    // if(listContact.size()>0 && listContact!=null)
    // No need to test for size() or null
        insert listContact;
}

That having been said, it's unclear to me what you intended to do with your map to begin with. Unless you've omitted part of your code, the map serves no purpose for the code in your trigger as the number of locations isn't used. Did you intend to nest the location number within a for loop to further sort your contacts or add this information to the contact perhaps?

5
  • 4
    Actually, I think he wants to create multiple contacts by Account depending on the NumberofLocations__c field. This is what the second for loop is supposed to do. Commented Apr 10, 2017 at 12:55
  • 1
    I agree and was editing my answer with comments along those lines. If that were the case, He'd need to know more than the number of locations. He'd need to have some kind of a map of locationId to acctId.
    – crmprogdev
    Commented Apr 10, 2017 at 12:59
  • @MartinLezer you are right..thats the scenario
    – Annappa PH
    Commented Apr 10, 2017 at 15:26
  • @crmprogdev I didnt understand this line from you code..consider In account no.of location is 3...Interger i=0 is fine then, for(Id accId:mapAcc.keyset()) ...mapAcc.keyset() contains accountId....again you increment the 'i'...when it will come out from the for loop?...how you decide how many times your for loop as to run?!!!...
    – Annappa PH
    Commented Apr 10, 2017 at 15:40
  • 1
    See List or Set Iteration For Loops in the docs. It iterates over each element in the keyset and knows to stop when none remain in the set.
    – crmprogdev
    Commented Apr 10, 2017 at 19:03

You must log in to answer this question.

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