function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
ahmadkhanahmadkhan 

MAP inside a MAP

Hi people!

 

i have a question regarding MAPS i am using MAP inside a map for the first time in apex and having some hard time can anyone help me?

 

Map<String, Map<String, List<Audiometry_Test__c>>> audiometryTest =
                                                    new Map<String, Map<String, List<Audiometry_Test__c>>>();

 

how to loop through all the values of this map inside map?

sfdcfoxsfdcfox
for(String key1:audiometryTest.keySet()) {
  for(String key2:audiometryTest.get(key1).keySet()) {
    for(Audiometry_Test__c record:audiometryTest.get(key1).get(key2)) {
      // map<key1, map<key2, list<test>>>
    }
  }
}

Basically, it's no different than a single dimension, just repeated.

ahmadkhanahmadkhan
thanks for the reply but nested for is not a good practise right? any other solution except using Nested for loops
liron169liron169

Hello,

 

I think you need to understand first that you don't have single map inside. you have map for each key.

 

Your map looks something like this:

 

 

audiometryTest {
    key1    =>    Map{    subkey1 => list,
                subkey2 => list,
                subkey3 => list},

    key2    =>    Map{    subkey4 => list,
                subkey5 => list},

    key3    =>    Map{    subkey6 => list,
                subkey7 => list,
                subkey8 => list}
}

 

 

 

For each key in the main map, you have map, therefore to loop on all the values in all the maps, it must be nseted loop

ahmadkhanahmadkhan
yeah i know so it means no solution except nested for loops right?
liron169liron169


Perhaps you can store your data in single map, than you won't need nested loop.
Of couse, you need first to consider how it will affect your code.

for my above example, I can use this:

audiometryTest {
    key1subkey1 => list,
    key1subkey2 => list,
    key1subkey3 => list,
    key2subkey4 => list,
    key2subkey5 => list,
    key3subkey6 => list,
    key3subkey7 => list,
    key3subkey8 => list
}

ahmadkhanahmadkhan
and then how you are going retrieve key1 values for subkey1??
liron169liron169
In general, when looping you won't need key1 or subkey1, the idea is when building this map to use concatenation of the keys to create it.

If you need each key in other places in the code, you might issues, and I mention that you will need to consider how it can effect your code.
If it's too complicated, than leave it with map inside map, I don't see problem with the nested loop.
sfdcfoxsfdcfox

The advice about not using a for loop inside another for loop only applies to lists and sets, not maps. This is because lists and sets create multiplictive iterations, while maps create near-linear iterations. The former is extremely wasteful and doesn't scale well, while the latter is only marginally less efficient than a flat list.

 

Here's why you don't use a list-by-list loop:

 

Account a = [select id from account];
Opportunity o = [select id,accountid from opportunity where accountid in :a];
for(opportunity opp:o) {
  for(account acc:a) {
    if(opp.accountid==acc.id) {
      // Do something with the opportunity and matching account
    }
  }
}

If 200 accounts are queried, and one opportunity for each account, this iteration will use 40,000 script statements plus whatever logic is applied for each matching account-opportunity pair.

 

By using a map, we can reduce this to 1000 script statements plus whatever logic is applied to each matching pair:

 

map<id,map<id,opportunity>> ao = new map<id,map<id,opportunity>>();
map<id,account> ac = new map<id,account>([select id from account]);
for(Opportunity record:[select id,accountid from opportunity where accountid in :ac.keyset()]) {
  if(!ao.containskey(record.accountid)) {
    ao.put(record.accountid,new map<id,opportunity>());
  }
  ao.get(record.accountid).put(record.id,record);
}

for(Id accountId:ao.keyset()) {
  for(Id oppId:ao.get(accountId).keyset()) {
    // ac.get(accountid) and ao.get(accountid).get(oppid)
    // are account and opportunity, respectively.
  }
}

(NOTE: This isn't actually a good reason to use this type of loop, but it's illustrative of efficency).

 

This loop is far more efficient because each opportunity is pre-associated with the account, so each inner loop executes for only the number if inner iterations, which in my example was said to be 1:1, so the each inner loop runs once per outer loop. It's linear: If there are 2000 of each, the total usage would still only be 10000 script statements; the list-by-list loop would use 4000000 (4 million!) script statements.

 

Now, instead of using ID values, which we know would be far more efficient to just use a single map with a loop over the child objects, if it were a collection of random strings, for example, a part family and a product code, we could still efficiently store and retrieve the data and even process all of the elements within at the cost of only one extra script statement per outer element.

 

Note that both scripts make the general assumption that you're querying first, then processing the data. It is still never appropriate to query data inside a loop. In other words, do not do this:

for(Account record:Trigger.new)
  for(Opportunity oppRecord:[SELECT Id FROM Opportunity WHERE AccountID = :record.id]) {
    // do something here
  }
}

So, the moral of the story is to make sure that you're using the tools you have available correctly.