0

I tried to make use of maps but still getting CPU time error.

Code:

 //@AuraEnabled
public static Map<String, Integer> getDateRangeMap()
{
    Map<String, Integer> mapToReturn = new Map<String, Integer>();
    Integer mnth = System.Today().MOnth();
    Integer yr = System.Today().Year();
    Integer previousmonth = Date.today().addMonths(-1).month();
     Integer nextmonth = Date.today().addMonths(1).month();
            List<customobj__c> myCalls = new List<customobj__c>([SELECT Id,  Name, Date__c FROM customobj__c where owner.Id =: UserInfo.getUserId() and CALENDAR_YEAR(Date__c)= :yr and (CALENDAR_MONTH(Date__c)= :previousmonth or CALENDAR_MONTH(Date__c)= :nextmonth or CALENDAR_MONTH(Date__c)= :mnth ]);
   .
    List<Opportunity> lstOpp = new List<Opportunity>([select Id, Name, closeDate from Opportunity where closeDate != NULL AND StageName!= 'Won - Closed' AND StageName!= 'Closed - No Win' AND ownerId =: UserInfo.getUserId()and CALENDAR_MONTH(closeDate)= :mnth and CALENDAR_YEAR(closeDate)= :yr]); 
    List<Event> lstEvent = new List<Event>([SELECT AccountId,EndDateTime,Id,StartDateTime,Subject FROM Event  where ownerId =: UserInfo.getUserId() and CALENDAR_MONTH(StartDateTime)= :mnth and CALENDAR_YEAR(StartDateTime)= :yr]);

    //processing the data for the map. 
    //Logic - Iterate through all dates{Iterate over list--Transform the date with the string--iterate for its count}
    for(customobj__c objCR : myCalls){
        mapToReturn.put(objCR.Date__c.format(), 0);
        //system.debug('mapToReturn CR'+mapToReturn);
    }

    for(Opportunity oOpp : lstOpp){
        mapToReturn.put(oOpp.closeDate.format() , 0);
         system.debug('mapToReturn opp'+mapToReturn);
    }

    for(Event oEvt : lstEvent)
    {
        if(oEvt.StartDateTime != NULL)
        {
            mapToReturn.put(oEvt.StartDateTime.date().format() , 0);  
             system.debug('mapToReturn Evt SD'+mapToReturn);
        }
        if(oEvt.EndDateTime != NULL)
        {
            mapToReturn.put(oEvt.EndDateTime.date().format() , 0);
             system.debug('mapToReturn Evt ED'+mapToReturn);
        }

        //we got all unique dates now. Now, we'll iterate through all the obj loops to get count
        for(String oStr : mapToReturn.keySet())
        {
            //convert string into date again.
            system.debug('oStr'+oStr);
            Date d = Date.parse(oStr);
            Integer count = mapToReturn.get(oStr);
            system.debug('!!!!!count'+count);
            for(customobj__c objCR : myCalls)
            {
                if(objCR.Date__c != NULL ){
                    if( d.isSameDay(objCR.Date__c) ){
                        count = count + 1;
                    }    
                }
            }

            for(Opportunity oOpp : lstOpp){
                if(oOpp.CloseDate != NULL ){
                    if(d.isSameDay(oOpp.CloseDate) ){
                        count = count + 1;
                    }
                }
            }

            for(Event oEvtt : lstEvent){
                if(oEvtt.StartDateTime != NULL){

                    if(oEvtt.EndDateTime == NULL){
                        if( d.isSameDay(oEvtt.StartDateTime.date())){
                            count = count + 1;
                        }
                    }else{
                        if(oEvtt.StartDateTime.date().daysBetween(d) >=0 && oEvtt.EndDateTime.date().daysBetween(d) <= 0 ){
                            count = count + 1;
                        }
                    }
                }
            }

          mapToReturn.put(oStr, count);


        }

    }
    System.debug(mapToReturn);
    return mapToReturn;
}

Here is the updated logic:

 Map<Date, Integer> myMap = new Map<Date, Integer>();  
  Integer mnth = System.Today().MOnth();
  Integer yr = System.Today().Year();
  Integer previousmonth = Date.today().addMonths(-1).month();
  Integer nextmonth = Date.today().addMonths(1).month();

  for(AggregateResult aggregate3:[SELECT day_only(StartDateTime), count(Id) records1   FROM Event WHERE owner.Id =: UserInfo.getUserId() and CALENDAR_YEAR(StartDateTime)= :yr and (CALENDAR_MONTH(StartDateTime)= :previousmonth or CALENDAR_MONTH(StartDateTime)= :nextmonth or CALENDAR_MONTH(StartDateTime)= :mnth )  GROUP BY day_only(StartDateTime)]){
    Date key = (Date)aggregate3.get('expr0');
    Integer value = (Integer)aggregate3.get('records1');
    myMap.put(key,value);
 }

    for(AggregateResult aggregate :[SELECT Date__c, count(Id) records  FROM myobject__c WHERE owner.Id =: UserInfo.getUserId() and CALENDAR_YEAR(Date__c)= :yr and (CALENDAR_MONTH(Date__c)= :previousmonth or CALENDAR_MONTH(Date__c)= :nextmonth or CALENDAR_MONTH(Date__c)= :mnth )  GROUP BY Date__c]){
 Date key = (Date)aggregate.get('Date__c');
 Integer value = (Integer)aggregate.get('records');
 myMap.get(key) += value;
}

for(AggregateResult aggregate2:[SELECT closeDate, count(Id)  FROM Opportunity WHERE owner.Id =: UserInfo.getUserId() and CALENDAR_YEAR(closeDate)= :yr and (CALENDAR_MONTH(closeDate)= :previousmonth or CALENDAR_MONTH(closeDate)= :nextmonth or CALENDAR_MONTH(closeDate)= :mnth )  GROUP BY closeDate]){
 Date key = (Date)aggregate2.get('closeDate');
 Integer value = (Integer)aggregate2.get('expr0');
 myMap.get(key) += value;

}
3
  • Consider moving this to javascript if you're getting CPU errors. Use the controller to return the record lists back to the component and let the component handle the rest.
    – gNerb
    Oct 16, 2017 at 15:14
  • Please note that creating duplicate accounts to circumvent a question ban is likely to get you into further trouble. I suggest you request to merge your account with this likely duplicate rather than getting caught and further punished.
    – Adrian Larson
    Oct 16, 2017 at 16:15
  • 2
    debug statements consume cpu time
    – cropredy
    Oct 16, 2017 at 18:18

1 Answer 1

2

You're doing several things which are inefficient:

  • Don't instantiate a new list with query results. A query already returns a list.

    Instead of:

    List<MyObject__c> records = new List<MyObject__c>([/*query*/]);
    

    Just use:

    List<MyObject__c> records = [/*query*/];
    
  • Casting from date to string and then back to date is just wasting time. You get the date values from your records, then save the value as a string, but when you retrieve it you actually need the date. Save yourself the data-type-roundtrip.

    Instead of:

    Map<String, Integer> myMap = new Map<String, Integer>();
    // ...
    myMap.put(someDate.format(), 0);
    // ...
    for (String key : myMap.keySet())
    {
        Date dateKey = Date.parse(key);
        // converting back and forth like this is inefficient
    }
    

    Use:

    Map<Date, Integer> myMap = new Map<Date, Integer>();
    // ...
    myMap.put(someDate, 0);
    // ...
    for (Date dateKey : myMap.keySet())
    {
        // now you already have the date value
    }
    
  • Aggregate queries can do a lot of the computing for you without counting against CPU Time. Something like:

    for (AggregateResult aggregate : [
        SELECT MyDateField__c, count(Id) records
        FROM MyObject
        WHERE ...
        GROUP BY MyDateField__c
    ]){
        Date key = (Date)aggregate.get('MyDateField__c');
        Integer value = (Integer)aggregate.get('records');
        myMap.get(key) += value;
    }
    
  • You don't need daysBetween to do comparison, nor to even to get the Date instance from the Datetime.

    Instead of:

    if (startDatetime.date().daysBetween(myDate) >= 0 && endDatetime.date().daysBetween(myDate) <= 0)
    

    Just use:

    if (startDatetime <= myDate && endDatetime >= myDate)
    

As noted in the comments:

  • Remove all your debug statements. They consume CPU Time.
  • Make sure you are not logging at a too-fine level of granularity either. In fact, if your user has a trace flag, set all debug levels to the lowest value.
2
  • Hi Adrian, Thanks for the suggestions. I have updated my logic by using aggregated result but when I am trying to assign integer value to the map by getting the key, I am hitting error saying 'Expression cannot be assigned' when using myMap.get(key) += value; Anyway we could add the count value to the Map value of specific key? Oct 16, 2017 at 19:58
  • Please update your question with what you tried...
    – Adrian Larson
    Oct 16, 2017 at 19:59

You must log in to answer this question.

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