0

I am currently working on a Lightning Component. Within my Apex Controller I have a nested map looking like this:

Map<LineItem, Map<LineItem, Map<LineItem, Map<String, LineItem>>>> resultMap = new Map<LineItem, Map<LineItem, Map<LineItem, Map<String, LineItem>>>>();

I am able to populate the map within apex, but when I open up console.log(response.getReturnValue()); in my JavaScript Controller the console only displays LineItemController$LineItem.

I have tried different approaches found on StackExchange, unfortunately I wasn't successful. Anybody has an idea how I can display all of the key/value pairs in the nested map?

Here is some example code:

APEX Controller:

public class LineItemController {

// Quick Method for creating the map    
    @AuraEnabled
    public static Map<LineItem, Map<LineItem, Map<LineItem, Map<String, LineItem>>>> testNestedMap() { 
        Map<LineItem, Map<LineItem, Map<LineItem, Map<String, LineItem>>>> resultMap = new Map<LineItem, Map<LineItem, Map<LineItem, Map<String, LineItem>>>>();
        
        List<LineItem> lineItemList = new List<LineItem>(); 
        List<LineItem> lineItemList2 = new List<LineItem>(); 
        List<LineItem> lineItemList3 = new List<LineItem>(); 
        List<LineItem> lineItemList4 = new List<LineItem>(); 
        
        lineItemList = createLineItem(1);
        lineItemList2 = createLineItem(10);
        lineItemList3 = createLineItem(100);
        lineItemList4 = createLineItem(100000);
   
        for (LineItem li : lineItemList) {
            resultMap.put(li, new Map<LineItem, Map<LineItem, Map<String, LineItem>>>());   
            for (LineItem li2 : lineItemList2) {
                resultMap.get(li).put(li2, new Map<LineItem, Map<String, LineItem>>());   
                for (LineItem li3 : lineItemList3) {
                    resultMap.get(li).get(li2).put(li3, new Map<String, LineItem>());  
                    for (LineItem li4 : lineItemList4) {
                        resultMap.get(li).get(li2).get(li3).put(li4.code, li4);      
                    }
                }   
            }
        }
        system.debug(resultMap);      
        
        return resultMap; 
    }
    
    private static List<LineItem> createLineItem(Integer j) {
        
        List<LineItem> lineItemList = new List<LineItem>(); 
        
        for(Integer i=1; i < 4; i++) {
            LineItem li = new LineItem();
            li.amount=100.0 * Integer.valueof((Math.random() * 10));
            li.code= String.valueOf(i*j);
            li.name='Item ' + (i*j);
            li.revenue=100.0 * Integer.valueof((Math.random() * 10));
            lineItemList.add(li);
        }
        
        return lineItemList;
        
    }
    

    public class LineItem {
        public String code {get; set;}
        public String name {get; set;}
        public Decimal amount {get; set;}
        public Decimal revenue {get; set;}

        public LineItem() {
            this.amount = 0.0;
            this.revenue = 0.0;
        }
    }
}

JavaScript-Controller:

({
    doInit : function(component, event, helper) {
        
        var action = component.get("c.testNestedMap");      
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                console.log(response.getReturnValue());
            } else {
                console.log(response.error[0].message);
            }
        });
        $A.enqueueAction(action);   
    }
})

And last but not least the component:

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId,force:appHostable" access="global" controller="LineItemController">

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:component>

3 Answers 3

3

You may want to try JSON/Wrapper approach.

  1. JSON - You can pass JSON string from your current apex method instead of sending nested maps with simple statement - JSON.serialize( nestedMapWhichIsInReturn );.

  2. Wrapper - User wrapper to store all respective maps and send its instance to lightning. However it's also kind of JSON only but you can get some of the variable names to update in the lightning controller.

Curious to know the actual use-case which has so many maps. If you can mention use case some other ways can be suggested by community to remove complexity(like you're iterating 3-4 nested loops).

1
  • I described the use-case in a new answer post.Thanks Chris
    – Chris
    Commented Aug 28, 2020 at 10:16
0

First of thanks for your answer, Ysr Shk.

I created a new answer instead of a comment since I try to explain the use-case and need a little bit of space.

I have an object containing the following fields, while all of them a required fields:

  • Name
  • TopLevelCode (1-digit number)
  • SecondLevelCode (2-digit number)
  • ThirdLevelCode (3-digit number)
  • BaseLevelCode (6-digit code)
  • Amount
  • Revenue

Some example records look like this:

Name; TopLevelCode; SecondLevelCode; ThirdLevelCode; BaseLevelCode; Amount; Revenue
Item 1; 0; 01; 012; 012345; 100; 40000
Item 2; 0; 01; 012; 012789;80; 30000
Item 3; 0; 02; 025; 025025; 200; 100000
Item 4; 9; 90; 902; 902123; 50; 2000
Item 4; 9; 90; 903; 903345; 15; 75000

The lightning component should display a kind of a sorted table, while the component should sum up related codes. In the example above it should display the following when opening the component:

  • 0 – Amount: 380 – Revenue: 170000 (sum of amount & revenue for all items with topLevelCode 0)
  • 9 – Amount: 65 – Revenue: 77000 (sum of amount & revenue for all items with topLevelCode 9)

When clicking on one of the topLevelCodes the secondLevelCodes should open. Clicking on a secondLevelCode it should open thirdLevelCodes and so on. The final result for the given example should look like this when everything is expanded:

  • 0 – Amount: 380 – Revenue: 170000

    • 01 – Amount: 180 – Revenue: 70000
      • 012 – Amount: 180 - Revenue: 70000
        • 012345 – Amount: 100 - Revenue: 40000
        • 012789 – Amount: 80 – Revenue: 30000
    • 02 – Amount: 200 – Revenue: 100000
      • 025 – Amount: 200 - Revenue: 100000
        • 025025 – Amount: 200 - Revenue: 100000
  • 9 – Amount: 65 – Revenue: 77000

    • 90 – Amount: 65 - Revenue: 77000
      • 902 – Amount: 50 - Revenue: 2000
        • 902123 – Amount: 50 - Revenue: 2000
      • 903 – Amount: 15 - Revenue: 75000
        • 903345 – Amount: 15 - Revenue: 75000

Within my Apex Controller I currently do all the calculations and put it the map, so that there is only one return parameter. I tried to minimize the calls from JS to Apex.

I hope the given example is understandable :)!

Thanks Chris

1
0

A Map object is serializable into JSON only if it uses one of the following data types as a key.

  1. Boolean
  2. Date
  3. DateTime
  4. Decimal
  5. Double
  6. Enum
  7. Id
  8. Integer
  9. Long
  10. String
  11. Time

that's why you are facing issues in getting the JSON correctly. please try using a Map key as one of the above data types.

Maps Documentation: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections_maps.htm

Using Custom types: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections_maps_keys_userdefined.htm

Note: if you are using an sObject as a key then the uniqueness is determined on the basis of the field values, but here you are using a custom Apex class type then you must have to implement equals and hashcode contracts(just an extra information)

You must log in to answer this question.

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