First, Maps are defined with two data types, such that we would express this in generic terms written with a unique letter for each data type involved: Map<T, U>, or as the documentation states: Map<T1, T2>. I prefer the classic nomenclature, but choose whichever you feel you understand better.
In your case, T is Id, and U is Map<T′, U′>. T′ and U′ are the type String. If you remember that, it should be easier to construct complicated types in the future:
Map< T , U >
Map< Id, Map< T′ , U′ > >
Map< Id, Map< String, String > >
Second, to populate nested structures, you have to put them inside each other. In other words, before you can populate a value in the inner map, the outer map must have the object created first. To create most objects, we use the new keyword. One new is one new object.
When we create a new object, we call a constructor, which can take zero or more parameters to configure the initial state of the object. They are normal methods, which means we need to pass in any parameters, like we would any other method. new Map<Id, Map<String, String>>(), for example, constructs a new object with the compound data type Map<Id, Map<String, String>>.
So, to populate such a Map, we would do something like:
Map<Id, Map<String, String>> myMap = new Map<Id, Map<String, String>>();
myMap.put(obj.Id, new Map<String, String>());
myMap.get(obj.Id).put('value1','value2');
Note that put displaces the previous value if it exists, so we normally check first:
Map<String, String> myMapValue = myMap.get(obj.Id);
if(myMapValue == null) {
myMapValue = new Map<String, String>();
myMap.put(obj.Id, myMapValue);
}
myMapValue.put('value1','value2');
You can initialize a Map with the {} constructor format:
Map<Id, Map<String, String>> myMap = new Map<Id, Map<String, String>> {
obj.Id => new Map<String, String> {
'value1' => 'value2'
}
};
Notice how there are two new keywords. We are still creating new objects, just placing one inside the other initially. The => operator simply means "key => value." This is the only use of => in Apex at this time.
Note that putAll is used to put a bunch of values inside the map at once. It requires that the parameter provided is the same type as U from our example above.
Map<Id, Map<String, String>> myMap = new Map<Id, Map<String, String>>();
Map<String, String> params = new Map<String, String>();
params.put('value1','value2');
myMap.put(obj.Id, new Map<String, String>());
myMap.get(obj.Id).putAll(params);
Answer from sfdcfox on Stack ExchangeUsing Maps in Apex (beginner)
triggers - Apex Map within a Map - Stack Overflow
apex - I have a Map<String, Account> I want to insert the account in Salesforce and Create a new Map<String,accId> - Stack Overflow
apex - How to add value in map of map? - Salesforce Stack Exchange
First, Maps are defined with two data types, such that we would express this in generic terms written with a unique letter for each data type involved: Map<T, U>, or as the documentation states: Map<T1, T2>. I prefer the classic nomenclature, but choose whichever you feel you understand better.
In your case, T is Id, and U is Map<T′, U′>. T′ and U′ are the type String. If you remember that, it should be easier to construct complicated types in the future:
Map< T , U >
Map< Id, Map< T′ , U′ > >
Map< Id, Map< String, String > >
Second, to populate nested structures, you have to put them inside each other. In other words, before you can populate a value in the inner map, the outer map must have the object created first. To create most objects, we use the new keyword. One new is one new object.
When we create a new object, we call a constructor, which can take zero or more parameters to configure the initial state of the object. They are normal methods, which means we need to pass in any parameters, like we would any other method. new Map<Id, Map<String, String>>(), for example, constructs a new object with the compound data type Map<Id, Map<String, String>>.
So, to populate such a Map, we would do something like:
Map<Id, Map<String, String>> myMap = new Map<Id, Map<String, String>>();
myMap.put(obj.Id, new Map<String, String>());
myMap.get(obj.Id).put('value1','value2');
Note that put displaces the previous value if it exists, so we normally check first:
Map<String, String> myMapValue = myMap.get(obj.Id);
if(myMapValue == null) {
myMapValue = new Map<String, String>();
myMap.put(obj.Id, myMapValue);
}
myMapValue.put('value1','value2');
You can initialize a Map with the {} constructor format:
Map<Id, Map<String, String>> myMap = new Map<Id, Map<String, String>> {
obj.Id => new Map<String, String> {
'value1' => 'value2'
}
};
Notice how there are two new keywords. We are still creating new objects, just placing one inside the other initially. The => operator simply means "key => value." This is the only use of => in Apex at this time.
Note that putAll is used to put a bunch of values inside the map at once. It requires that the parameter provided is the same type as U from our example above.
Map<Id, Map<String, String>> myMap = new Map<Id, Map<String, String>>();
Map<String, String> params = new Map<String, String>();
params.put('value1','value2');
myMap.put(obj.Id, new Map<String, String>());
myMap.get(obj.Id).putAll(params);
Try this
Map<Id,Map<string,string> myMap = new Map<Id,Map<string,string>{}
myMap.put(obj.Id, new map<string, string>{'test' => 'test'});
More details here : https://stackoverflow.com/questions/9521740/apex-map-within-a-map
Using your sample code will work, though of course the second level map can only ever have one entry, if you're in a situation where you may need more then the code below will do the trick for you:
// This line creates the map in the proper format
Map<Id,Map<Id,Addendum__c>> addendums = new Map<Id,Map<Id,Addendum__c>>{};
// This for loop goes through each addendum and first places it into a single dimension map.
// Once that map is created, it is placed into the final multi-dimensional
for(Addendum__c addendum : [SELECT Id,Opportunity__c FROM Addendum__c WHERE Opportunity__c IN :oppBatch])
{
if(addendums.get(addendum.Opportunity__c) == null)
{
addendums.put(addendum.Opportunity__c, new Map<Id, Addendum__c>{addendum.Id => addendum);
}
else
{
addendums.get(addendum.Opportunity__c).put(addendum.Id, addendum);
}
}
As you can see this uses get() on the first map to get the correct second-level map in which we want to add an addendum.
The method that Josh provided works fine. If you'd like to remove one line of code, you can using the following approach:
Map<Id, Map<Id,Addendum__c>> addendums = new Map<Id,Map<Id,Addendum__c>>();
for(Addendum__c addendum : [SELECT Id,Opportunity__c FROM Addendum__c WHERE Opportunity__c IN :oppBatch]){
addendums.put(addendum.opportunity__c, new Map<Id, Addendum__c>{addendum.id => addendum});
}
I'm not aware of a more efficient approach than this one.
If you want to add a map onto existing map, use putAll() method.
finalMap.get('DETAILS').putAll(subDetails);
It will add the entries of subDetails onto whatever Map finalMap contains aganist key 'DETAILS'.
You have to add like below
finalMap.put('DETAILS',subDetails);
OR
Get the existing value and den update it like below
Map<string,List<string>> subDetails= finalMap .get('DETAILS')
//-- Do your business logic to update subDetails and then add as below
finalMap.put('DETAILS',subDetails);