0

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;
5
  • Shoot I still had it for when it was a list, I'll adjust. Commented Jan 9, 2017 at 20:53
  • I'm getting stuck, I get "Variable does not exist: Oct2014__c" when I try to save. I tried adding revPipe.Oct2014__c but then get 'Invalid field initializer' Commented Jan 9, 2017 at 20:58
  • How locked into this architecture are you? Any time you're making 47 parallel updates...it's pretty messed up. You should try to pursue something more scalable if you have any liberty to do so.
    – Adrian Larson
    Commented Jan 9, 2017 at 21:01
  • I'm sort of locked, chat? Commented Jan 9, 2017 at 21:03
  • Sure, let's chat...
    – Adrian Larson
    Commented Jan 9, 2017 at 21:09

1 Answer 1

3

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);
    }
}
2
  • Do you still need a DML statement after the for loop? Example: update pipelines? Commented Jan 11, 2017 at 4:23
  • Yes, you'll still need to actually perform the update.
    – Adrian Larson
    Commented Jan 11, 2017 at 4:24

You must log in to answer this question.

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