[SalesForce] Add duplicate keys to a list/map

I wrote a batch process over the weekend that looks at a child record's date field and updates the corresponding date on the parent record. I originally wrote it as a bunch of if statements and then added the parent id and field to a list. When I ran the batch today I get an error in debug log:

"Duplicate id in list"

This makes me think I need to add the values from the if statements to a map where I can update the record once with all the fields that it needs instead of updating the record multiple times for each possible value.

Here is a snip of batch code I have, how can I adjust to update a record once instead of multiple times.

global void execute(Database.BatchableContext BC, List<Revenue_Pipeline_Schedule__c> revPipeSchedule) {
    Map<Id, Revenue_Pipeline__c> revPipe = new Map<Id, Revenue_Pipeline__c>();

    for(Revenue_Pipeline_Schedule__c rPS : revPipeSchedule){
        if(rPS.Date__c == date.newinstance(2014,10,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Oct2014__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2014,11,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Nov2014__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2014,12,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Dec2014__c=rPS.Amount__c));}       
        if(rPS.Date__c == date.newinstance(2015,01,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Jan2015__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2015,02,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Feb2015__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2015,03,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Mar2015__c=rPS.Amount__c));}
        .... 47 lines later
}
update revPipe;

Best Answer

Any time you need to use a map for de-duplication, the basic idea is:

Map<Id, MyObject__c> records = new Map<Id, MyObject__c>();
//...
records.put(someMyObjectId, new MyObject__c(/*data*/));

However, in your case it is more complicated because you want to set multiple fields on the same record, so a scheme where you only use put won't work, because you will clobber your own updates. You can circumvent this problem, however, if you simply initialize all the keys first.

Map<Id, Revenue_Pipeline__c> pipelines = new Map<Id, Revenue_Pipeline__c>();
for (Revenue_Pipeline_Schedule__c schedule : scope)
{
    Id pipelineId = schedule.Revenue_Pipeline__c;
    pipelines.put(pipelineId, new Revenue_Pipeline__c(Id=pipelineId));
}
// now all keys are initialized

for (Revenue_Pipeline_Schedule__c schedule : scope)
{
    if (/*condition*/)
    {
        pipelines.get(schedule.Revenue_Pipeline__c).put(/*field*/, /*value*/);
    }
}

A note on your absurdly long if chain: you can almost always reduce these unwieldy bits of logic to use a Map. In this case, I would do a Map<Date, SObjectField> that tells you where to put the data.

static Map<Date, SObjectField> dateToField = new Map<Date, SObjectField>
{
    Date.newInstance(2014, 10, 1) => Revenue_Pipeline__c.Oct2014__c,
    Date.newInstance(2014, 11, 1) => Revenue_Pipeline__c.Nov2014__c,
    Date.newInstance(2014, 12, 1) => Revenue_Pipeline__c.Dec2014__c,
    // etc...
}

Then your above code can further simplify to just a few lines:

for (Revenue_Pipeline_Schedule__c schedule : scope)
{
    SObjectField field = dateToField.get(schedule.Date__c);
    if (field != null)
    {
        pipelines.get(schedule.Revenue_Pipeline__c).put(field, schedule.Amount__c);
    }
}
Related Topic