0

Hi I'm trying to create Map<String, Map<String, List>>.

I want it to represent "Map<Owner Name, Map<Status, List< Work Orders>>>". In the debug log it is adding every record to every map and I don't understand why. According to documentation when calling map.get(key) it should only add the value to that instance. Maybe I am misunderstanding or possibly I'm overlooking something but I can't figure out what.

public class NewReport_Controller
{
    public Map<String, Map<String, List<WorkOrder>>> AllOwnersToWos {get; set;}
    public Map <String, List<WorkOrder>> mapStatusToWorkOrders {get; set;}
    public Set<String> RecordOwners {get; set;}
    public Set<String> Statuses{get; set;}
    public List<WorkOrder> lstWOs {get; set;}
    
    public NewReport_Controller()
    {
        AllOwnersToWos = new Map<String, Map<String, List<WorkOrder>>>();
        mapStatusToWorkOrders = new Map <String, List<WorkOrder>>();
        RecordOwners = new Set<String>();
        Statuses = new Set<String>();
        lstWOs = [Select contract__r.ContractNumber, owner.LastName, owner.FirstName, status, serviceTerritory.name, serviceTerritory.Street, subject, invoice_amount__c from WorkOrder order by Status limit 7];
                  
        
        
        
        for (WorkOrder wo : lstWOs ) 
        {
            //Add full name to Record Owner Set / Add Status' to Status Set
            String name = wo.Owner.FirstName + ' ' + wo.Owner.LastName;           
            System.debug('Name: ' + name + ', Status: ' + wo.Status);
            Statuses.add(wo.status);
            List<WorkOrder> wos = new List<WorkOrder>();
            
            //create map<Owners, Map<Status, List<WorkOrder>>>
            if(AllOwnersToWos.containsKey(name))
            {
                //if map contains Status then add work order
                if(AllOwnersToWos.get(name).containsKey(wo.status))
                {
                    AllOwnersToWos.get(name).get(wo.status).add(wo);
                }
                else
                {
                    wos.add(wo);
                    AllOwnersToWos.get(name).put(wo.status, wos);
                }
            }
            else
            {
                wos.add(wo);
                mapStatusToWorkOrders.put(wo.status, wos);
                AllOwnersToWos.put(name, mapStatusToWorkOrders);
            }
        }
   }
}
5
  • Can you say what are you fetching with your .get and what is expected? Keep in mind that you haven't put the declaration of AllOwnersToWos and also that mapStatusToWorkOrders doesn't seem to be initialized inside the loop Feb 24, 2021 at 21:32
  • its initialized outside the loop. Ill add code. But I am expecting a name with the first .get() and a status with the second .get(). In the debug logs, lets say the work order record is owned by John, its adding it to the Map that contains the user Mark.
    – ThatGuy
    Feb 24, 2021 at 21:35
  • I don't know if it is "legal", but I would advise against using a Map key that has spaces in it. I can't reference anything to back up my position, but my gut is telling me that this is trouble waiting to happen....
    – Moonpie
    Feb 24, 2021 at 21:41
  • What do you mean it's adding every record to every map? This is troubleshooting 101. Is AllOwnersToWos getting the right keys? if so, for each 'name', are there the right statuses?
    – Nick Cook
    Feb 24, 2021 at 22:11
  • Yes the keys are correct for each map, both the names map and status map. Issue is when a new Owner name appears(such as Mark), all the work orders from John's map are suddenly getting put in Mark's map. It copied this function for each status as well.
    – ThatGuy
    Feb 25, 2021 at 1:35

1 Answer 1

2

I'd say that your problem is that you are reusing the map mapStatusToWorkOrders which is added as "reference" to the different places, hence... you end-up with a bunch of maps with the same data.

Maybe the following code is closer to what you are looking for? (it could contain some typos, as it was done without compiling), but hopefully the idea is clear (and hopefully this is also what you are looking for)

public class NewReport_Controller
{

    // assuming you want to have all Map<String /*Owner Name*/, Map<String /*Status*/, List<WorkOrder>>>
    public Map<String, Map<String, List<WorkOrder>>> AllOwnersToWos {get; set;} 

    // assuming you want to have all Map<String /*Status*/, List<WorkOrder>> (regardless of who is the owner)
    public Map <String, List<WorkOrder>> mapStatusToWorkOrders {get; set;}

    // assuming you want to have all Record Owners
    public Set<String> RecordOwners {get; set;}

    // assuming you want to have all Statuses
    public Set<String> Statuses{get; set;}

    // assuming you want to have all WorkOrders
    public List<WorkOrder> lstWOs {get; set;}
    
    public NewReport_Controller()
    {
        AllOwnersToWos = new Map<String, Map<String, List<WorkOrder>>>();
        mapStatusToWorkOrders = new Map <String, List<WorkOrder>>();
        RecordOwners = new Set<String>();
        Statuses = new Set<String>();
        lstWOs = [
            SELECT
                Status, Subject, Invoice_amount__c,
                Contract__r.ContractNumber,
                Owner.Name, // this gives you the concatenation, and prevents from having names like 'null Doe'
                ServiceTerritory.Name, ServiceTerritory.Street
            FROM WorkOrder 
            ORDER BY Status 
            LIMIT 7
        ];

        for (WorkOrder wo : lstWOs ) 
        {
            //Add full name to Record Owner Set / Add Status' to Status Set
            System.debug('Name: ' + wo.Owner.Name + ', Status: ' + wo.Status);

            RecordOwners.add(wo.Owner.Name);
            Statuses.add(wo.status);

            if(!AllOwnersToWos.containsKey(wo.Owner.Name)) 
            { // if its the first time we have this owner, we initialize and give an empty Map
                AllOwnersToWos.put(wo.Owner.Name, new Map<String, List<WorkOrder>>());
            }

            // we get the Map related to the current Owner (as it goes by-reference, we don't need to update it back)
            Map<String, List<WorkOrder>> currentOwnerStatusMap = AllOwnersToWos.get(wo.Owner.Name);

            if (currentOwnerStatusMap.containsKey(wo.Status)) 
            { // if the currentOwner Map has the current status, we add the WO to the list
                currentOwnerStatusMap.get(wo.Status).add(wo);
            } 
            else 
            { // otherwise we create a new list with the WO
                currentOwnerStatusMap.put(wo.Status, new List<WorkOrder>{wo});
            }

            if (mapStatusToWorkOrders.containsKey(wo.status))
            { // if the complete map contains the status, we add the WO to the list
                mapStatusToWorkOrders.get(wo.Status).add(wo);
            }
            else
            { // otherwise, we create a new list with the WO
                mapStatusToWorkOrders.put(wo.Status, new List<WorkOrder>{wo});
            }
        }


        // optionally, if you need it "Squared" because you are going to iterate in a visualforce page 
        // over owner and status and don't want to get null pointer exceptions when iterating over something like
        // {!AllOwnersToWos[OwnerVar][StatusVar]}
        for (String forOwner : RecordOwners)
        {
            if (!AllOwnersToWos.containsKey(forOwner))
            {
                AllOwnersToWos.put(forOwner, new Map<String, List<WorkOrder>>());
            }
            Map<String, List<WorkOrder>> currentOwnerStatusMap = AllOwnersToWos.get(forOwner);
            for (String forStatus : Statuses)
            {
                if (!currentOwnerStatusMap.containsKey(forStatus))
                {
                    currentOwnerStatusMap.put(forStatus, new List<WorkOrder>());
                }
            }
        }
    }
}

Keep in mind that when you play with Maps and List of non-Primitive types, and you add / put / change some data on them, you are having a reference, not a copy of the object, so any change done on them (unless cloned), they all be affected.

This is a powerful way of playing with data, as it allows you to have the same data structured in different ways without having to worry about where each copy is.

1
  • Thanks Sergio! This was the issue. I couldn't find anything online about this. You have just made me a more powerful developer! Much appreciated
    – ThatGuy
    Feb 25, 2021 at 1:44

You must log in to answer this question.

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