You have only declared the Map and never initialized it before adding values into it. You have to initialize the IdValueMap in the start method or Constructor as below and you will not get the Exception.
IdValueMap = new Map<id , map<String, String>>();
Answer from Saroj Bera on Stack Exchangeapex - Maps: adding more (related) sObjects than key values using PUTALL method - Salesforce Stack Exchange
APEX: How to put Map inside map - Salesforce Stack Exchange
apex - How to add value in map of map? - Salesforce Stack Exchange
java - Apex: map.Put() byVal instead of byRef? - Stack Overflow
PutAll only works on the ID field of an SObject. It is syntatic sugar for:
for(SObject record: someList)
someMap.put(record.Id, record);
To use any other type of mapping, you would have to make your own for loop.
Such a for loop would look like:
Map<Id, Contract[0]> someMap = new Map<id, contract[0]>();
... load account ids here ...
for(Contract record: [SELECT Id, AccountId, random_field FROM Contract WHERE AccountId IN :someMap.keyset()]) {
if(someMap.get(record.AccountId) == null) {
someMap.put(record.AccountId, new Contract[0]);
}
someMap.get(record.AccountId).add(record);
}
In your specific case, I might consider using a subquery:
someMap.putAll([SELECT Id, (SELECT Id, random_field FROM Contracts) FROM Account WHERE Id IN :someMap.keySet()]);
You can access a list of contracts via:
for(Contract record: someMap.get(accountId).Contracts) ...
So, basically, you would like to have a map of key-value pairs, where key is the AccountID, and the value is list of Contracts related to the Account? If so, first of all, rather than having Map < id, contract >, what you are really looking is Map < id, List < Contract > >.
Further on, you could store the list of id's - in the list. And only then use the list of ids to query the contracts and populate the map:
map<id, contract> accIDtoContract = new map<id, List<contract>>();
List<id> accIDs = new List<id>();
for( Usage_Data__c UD : Usage_Data_List ){
// Don't store AccountID in the list if it was already stored
if(accIDs.contains(UD.Account__c))
continue;
accIDs.add(UD.Account__c);
accIDtoContract(UD.Account__c, new List<Contract>());
}
List<contract> contracts = [SELECT ID, AccountID, random_field FROM Contract WHERE AccountID IN :accIDs]);
for(Contract c : contracts) {
accIDtoContract.get(c.AccountID).add(c);
}
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
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);
you can modify your code like this.In this code I am creating a new instance for each map insert.
Map> result = new Map>(); List temp;
for(CustomObj__c i:customList){
for(CustomObj__c j:customList2){
if(j.id == i.id){
temp = new list<string>();
temp.add(j.Contact__r.Owner.Id);
temp.add(j.Contact__r.Id);
result.put(i.Deal__r.id, temp);
break;
}
}
}
I think you should do it this way
if(j.id == i.id){
List<string> temp = new list<string>();
temp.add(j.Contact__r.Owner.Id);
temp.add(j.Contact__r.Id);
result.put(i.Deal__r.id, temp);