[SalesForce] Compare 2 Lists and Remove Duplicates

I have an application which pulls through a set list of Placement__c records. The user is able to:

  • Use select boxes to select certain records.
  • Specify a start date
  • Specify an end date

Those that are selected should then create a Timecard__c record.

What I want to do is ensure that no duplicates are created based on the start date and end date and the Contact lookup in question!

At the moment I have the following:

List<ts2__Placement__c> selectedPlacements = new List<ts2__Placement__c>();

for (pPlacement pPla : getPlacements()) {
  if (pPla.selected == true) {
    selectedPlacements.add(pPla.pla);
  }
}

if (!selectedPlacements.isEmpty()) {
  for (ts2__Placement__c pla : selectedPlacements) {
    System.debug(pla);
  }
}

Now I know I could run a query, something like this:

if (!selectedPlacements.isEmpty()) {
  for (ts2__Placement__c pla : selectedPlacements) {
    Integer checkExists = [SELECT Count() FROM Timecards__c 
                            WHERE Contractor__c = :pla.ts2__Employee__c AND
                                  Week_start_date__c = :startDate AND
                                  Week_end_date__c = :endDate];

      if (checkExists == 0) {
        // Do stuff!
      }
  }
}

But I know I'll quickly hit the SOQL governor limit if I do this.

What would be the most efficient way of checking if a record already exists and removing that record from the list so no duplicates are created?

Best Answer

My first thought is to turn to a pair of Set<Timecard__c>.

  • Start by gathering the set of Ids representing the contractors from the selected Placement__c records
  • create Timecard__c records (only in memory, no DML insert at this stage), setting Contractor__c, Week_Start_Date__c, and Week_End_Date__c. Put these records into a Set<Timecard__c>
  • Run a query for the Contact records matching those contractor Ids, and include a parent-child subquery for all related Timecard__c records
    • Make sure the subquery only pulls in the same fields as you set when creating the initial set of Timecard__c records
  • Iterate over the Contacts, and put all of the related Timecard__c records into a different set
  • Finally, set1.removeAll(set2)

The idea is that the hash function underlying the Set type should produce identical results if you pass in different object instances with exactly the same fields populated, and exactly the same values for those fields.

That allows you to use remove() or removeAll() to do the heavy lifting.

In testing this myself, the general idea works but a query will also pull the Id and RecordTypeId, which means you'd need to iterate over the related Timecard__c records and explicitly create new Timecards that omit the Id and RecordTypeId rather than just being able to directly addAll() on the second set from the list of related Timecard__c records on the Contact.

code would look like this:

// startDate and endDate defined elsewhere
List<ts2__Placement__c> selectedPlacements = new List<ts2__Placement__c>();
Set<Id> contactIds = new Set<Id>();
Set<Timecard__c> timecards = new Set<Timecard__c>();
Set<Timecard__c> existingTCs = new Set<Timecard__c>();

for (pPlacement pPla : getPlacements()) {
  if (pPla.selected == true) {
    selectedPlacements.add(pPla.pla);
    contactIds.add(pPla.ts2__Employee__c);
    timecards.add(
      new Timecard__c(
        Contractor__c = pPla.ts2__Employee__c,
        Week_Start_Date__c = startDate,
        Week_End_Date__c = endDate
      )
    );
  }
}

if (!selectedPlacements.isEmpty()) {
  for (Contact con :[SELECT Id, (SELECT Contractor__c, Week_Start_Date__c, Week_End_Date__c FROM Timecards__r) FROM Contact WHERE Id IN :contactIds]) {
    for(Timecard__c tc :con.Timecards__r){
      existingTCs.add(new Timecard__c(
      Contractor__c = tc.ts2__Employee__c,
      Week_Start_Date__c = tc.Week_Start_Date__c,
      Week_End_Date__c = tc.Week_End_Date__c);
    }
  }
}

timecards.removeAll(existingTCs);

insert timecards;
Related Topic