-2

I have a scenario in Apex controller wherein I need to create following map:
"Map<String, Map<String, List<CustomObject__c>>>"
The query is below:
SELECT Field1,Field 2 FROM CustomObject__c

It should create a map :
Map<Field1, Map<Field2, List<CustomObject__c>>>
Could anyone help achieving this.!!

I tried the following:

    if(!String.isBlank(accountId) ){     
        Map<String,List<Member_Measure__c>> measureMap=new Map<String,List<Member_Measure__c>>();

        List<Member_Measure__c> memList=new List<Member_Measure__c>();
        List<Member_Measure__c> tempmemList = new List<Member_Measure__c>();
        for(Member_Measure__c mem : [SELECT id,Measure_Element__c,Value__c,Member__c, Member__r.Name,Measure_Element__r.Name,Name,Measure_Element__r.Measure__r.Name,
                                     Measure_Element__r.Measure__r.Domain__c FROM Member_Measure__c WHERE Member__c =: accountId])
        {
            if(NULL!=domainMemberMap.get(mem.Measure_Element__r.Measure__r.Domain__c)){
                if(NULL!=measureMap.get(mem.Measure_Element__r.Measure__r.Name)){
                    measureMap.get(mem.Measure_Element__r.Measure__r.Name).add(mem);
                }else{
                    tempmemList.add(mem);
                    measureMap.put(mem.Measure_Element__r.Measure__r.Name,tempmemList);                        
                }
                domainMemberMap.get(mem.Measure_Element__r.Measure__r.Domain__c).put(mem.Measure_Element__r.Measure__r.Name,memList); 
            }else{
                if(NULL!=measureMap.get(mem.Measure_Element__r.Measure__r.Name)){
                    measureMap.get(mem.Measure_Element__r.Measure__r.Name).add(mem);
                }else{
                    tempmemList.add(mem);
                    measureMap.put(mem.Measure_Element__r.Measure__r.Name,tempmemList);                        
                }
                domainMemberMap.put(mem.Measure_Element__r.Measure__r.Domain__c,measureMap);
            }
        }
    }
    return domainMemberMap;        
}

Here domainMemberMap is "Map<String, Map<String, List<Member_Measure__c>>>"
And I am getting the result in such a way that for each instance of MAP I am getting same values. So lets say the mapping is: Domain Name : Measure Name
D1 : M1
D1 : M2
D2 : M3

But what I am getting in result is this:
D1 : M1
D1 : M2
D1 : M3
D2 : M1
D2 : M2
D2 : M3

And hence the results are not as expected

2

Your code accumulates collections across list invocations and fails to use separate collections for each of the separate keys that you wish to track. The result is that your output data structures merge data across keys, because you never reset the collections

    List<Member_Measure__c> memList=new List<Member_Measure__c>();
    List<Member_Measure__c> tempmemList = new List<Member_Measure__c>();

in which you are accumulating values. Your logic is also overly repetitive, which makes it hard to understand what's happening and why it fails.

Luckily, you can simplify this rather dramatically. I'm not going to try to implement it for you, but I'll annotate the current logic to highlight where the issue lies and give you some pointers towards fixing it. I'm replacing these repeated, deep property values with local variables to make it easier to read.

    for(Member_Measure__c mem : [SELECT id, Measure_Element__c, Value__c, Member__c, Member__r.Name, Measure_Element__r.Name, Name, Measure_Element__r.Measure__r.Name,
                                 Measure_Element__r.Measure__r.Domain__c FROM Member_Measure__c WHERE Member__c =: accountId])
    {
         // Use local variables to simplify logic.
         String domain = mem.Measure_Element__r.Measure__r.Domain__c;
         String member = mem.Measure_Element__r.Measure__r.Name;

         if (NULL != domainMemberMap.get(domain)){
            if (NULL!=measureMap.get(member)){
                // When we add to the `List<Member_Measure__c>` returned by `get()`,
                // *which List is it*? We get our answer below.
                measureMap.get(measure).add(mem);
            } else {
                // What instance of `List<Member_Measure__c>` is `tempMemList`?
                // It's the same one you're using for _all_ measures.
                // As a result, _every value_ in `measureMap` is the same list.
                tempmemList.add(mem);
                measureMap.put(measure, tempmemList);    
                // Instead, you'd want to create a *new* List variable here.                    
            }
            // What is this doing here? We haven't mutated memList in this block.
            // In fact, memList is never used.
            domainMemberMap.get(domain).put(measure, memList); 
        }else{
            // This block is exactly the same as the block above,
            // and has the same problems.
            // The principle of "Don't Repeat Yourself" suggests 
            // you should condense this code. Hint: you don't need two levels
            // of `if` statement.

            if(NULL!=measureMap.get(measure)){
                measureMap.get(measure).add(mem);
            }else{
                tempmemList.add(mem);
                measureMap.put(measure,tempmemList);                        
            }

            // Here we have a different manifestation of the same problem:
            // You never create a new Map for each Domain value. You're
            // setting the same measureMap instance for *every* domain.
            // You already have the pattern for what you need to do 
            // in your List handling, once corrected.

            domainMemberMap.put(domain, measureMap);
        }
    }

Here's one quick example to get you started fixing this code. Any time you want to add a value to a nested collection - let's say a Map<String, List<Something>> - you can use this pattern:

Map<String, List<Something>> m = new Map<String, List<Something>>();

for (Something s: myListOfObjects) {
    if (!m.containsKey(s.Key__c)) {
         m.put(s.Key__c, new List<Something>());
    }
    m.get(s.Key__c).add(s);
}

That's all there is to it.

  • Are you using an inline SOQL not to exceed query limits? Also, I know I should just up-vote this but thank you for your quality contribution @David Reed – Atlas Can Dec 11 '19 at 6:29
  • @AtlasCan I took the inline SOQL from the poster's original code. I don't think there's any particular limits strategy to that piece (other than not running it in the loop, of course!). – David Reed Dec 11 '19 at 14:44
  • Thanks..!! It solved my purpose. – Sahil Jain Dec 12 '19 at 1:37
  • @DavidReed I thought you used this for this purpose, when querying large amount of records inline soql helps with the heap limit – Atlas Can Dec 12 '19 at 13:39
  • Oh, I see what you mean. Yes, that makes sense. – David Reed Dec 12 '19 at 16:03

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.