2

I am new to Apex and just starting to understand using maps to avoid loops within loops and hitting limits. My trigger below updates opp line items with the internal cost from another object using a map and the one value - "Service_Name".

My issue is I need to update another field on the Opp line item with "Agent Fee" in the same way - but there are 4 criteria to get the right Agent Fee from another object. I cant see how do so this other than have a loop of agent Fees object with the opp line loop? How else can I do this? Is it possible with Maps ?

trigger sectortest on Opportunity (after update) {

        for (Opportunity eachopportunity : Trigger.new){                              

        //Get costs for the Opps FY and put in map
        List<Internal_Costs__c> costs = [SELECT Id, Financial_Year__c, Service_Name__c, Standard_Cost__c  FROM Internal_Costs__c WHERE Financial_Year__c =: eachopportunity.Financial_Year__c]; 
        Map<string, Decimal> costmap = new Map<string, Decimal>();
        for (Internal_Costs__c eachcost : costs){                           
             costmap.put(eachcost.Service_Name__c, eachcost.Standard_Cost__c);               
         }       

        //Get Line Items for Opp
        List<OpportunityLineItem> opplines = new List<OpportunityLineItem>();  
        opplines = [SELECT Id, DS_Status__c, Country__c, Product2Id, UnitPrice FROM OpportunityLineItem WHERE OpportunityId =: EachOpportunity.Id];  
        List<OpportunityLineItem> opplinesupdate = new List<OpportunityLineItem>();      

        //assign cost to line items 
            for (OpportunityLineItem EachOppline : opplines) {                                                                         
                    decimal cost = costmap.get(EachOppline.Service_Name__c);                                            
                    EachOppLine.Internal_Cost__c = cost;                                                        
                    opplinesupdate.add(EachOppline);                                                                                                                                              
         }
             update opplinesupdate;               
        }                      
}
3
  • Where does this other field, Agent Fee exist? Is it on the same Internal_Costs__c object? Commented Mar 24, 2020 at 11:02
  • Hi - no its Agent Fees different object. And that object has 4 fields I need to match against from Opp line item fields to find right record.
    – Andy
    Commented Mar 24, 2020 at 14:50
  • It may be helpful to add an edit to your question to clarify that. Even if it's just a mock up of pseudo-logic of what you'd like (or what you'd do with for loops that you're looking to avoid). Commented Mar 24, 2020 at 15:20

1 Answer 1

5

This is typically handled using an object array as the key. Object arrays implement the methods required to use them as keys in maps quite efficiently (far more so than attempting to use a composite string key which is a common alternative that I strongly recommend against using).

For example, you might query your Agent Fee like:

List<AgentFee__c> fees = [
        SELECT Id, Value1__c, Value2__c, Value3__c, ...
            FROM AgentFee__c
            WHERE ...
];

You can then set up a key-based map like:

Map<Object[], AgentFee__c> agentFeesByKey = new Map<Object[], AgentFee__c>();

and populate it, using the specific key value fields from the fee, thus:

for (AgentFee__c fee : fees) {
    agentFeesByKey.put(new Object[] { fee.Value1__c, fee.Value2__c, fee.Value3__c },
            fee);
}

(If the combination of key value fields isn't unique the map may need to be from key to list of agent fees, but I'm assuming they are unique for this answer.)

Now when you get the opp lines you can find the agent fee for each line:

OpportunityLineItem line = ...;

AgentFee__c feeForLine = agentFeesByKey.get(
        new Object[] { line.Something__c,
                       line.SomethingElse__c,
                       line.SomethingElseAgain__c });

if (feeForLine != null) {
    // Now do something with the fee for this line
    ...
}

Adapt this with the specific key value fields and allow for non-unique combinations as required.

5
  • While I prefer the use of an sObject for the key, since you can't accidentally get things out of order, this is still a really cool tech I hadn't thought of. I might borrow this in the future :p
    – sfdcfox
    Commented Mar 29, 2020 at 16:00
  • I appreciate your approach too, and it does apply type safety directly. I do, as previously discussed, tend to shy away from using SObject as a key for various reasons, but for this scenario it certainly also works well.
    – Phil W
    Commented Mar 29, 2020 at 16:39
  • This has been super helpful and have got it working using this approach. Thank you so much!
    – Andy
    Commented Mar 31, 2020 at 14:38
  • @Andy, really pleased it was of use. Feel free to mark it as the best answer for future searches for similar problems.
    – Phil W
    Commented Mar 31, 2020 at 16:23
  • 1
    About 10 years a Salesforce developer and I had no idea we could do this! I have some horrible maps of maps that I'll now be able to replace with this method. Thanks, this is a game changer!
    – ministe
    Commented Oct 8, 2020 at 11:04

You must log in to answer this question.

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