1

I'm running into an interesting problem while trying to insert records from a class that runs on Insert and Update.

So when the parent record is created, we have some logic that looks at the parent and decides if child records should be created (we store these in a list), but we also run this code when the parent is updated...so the list contains duplicate records to insert - I believe this is because our SOQL queries to check for children fail when we're in the same context.

Is there any easy way to build a list/map/set of records to insert that won't contain duplicates (as in records that have the same record type and text field values).

Thanks in advance.

closed as unclear what you're asking by EricSSH, Raul, glls, battery.cord, Martin Lezer May 15 '18 at 12:27

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • We don't know how your building your map/list/set so if you post code we may be able to help you, Lists can contain duplicate values so you may want to use a Set.. – EricSSH May 14 '18 at 19:27
  • On this -- so the list contains duplicate records to insert -- do you insert and then update the parent in the same context? And that, when do you actually insert the child, after parent insert or update? Can you post some of your code here? – Jayant Das May 14 '18 at 21:03
1

I usually use a Map<Id, SObject> This works perfectly for updates.

Since you're inserting new records, you can use a related record ID instead of the ID of the record you are trying to insert (since it obviously wont have an Id yet).

Alternatively

If you have no related records, you can change the data type of the key to something else that maps up to the record.

The question is, what criteria are you to define a duplicate? If it is complex, you can try to create a hash of the criteria and insert that into a Map<String, SOBject>

For instance, if you are using both a date field and a user's Name, you could do something like the following:

Map<String, Bar__c> insertMap = new Map<String, Bar__c>();

for (Foo__c f : foos) {
    // String hash = f.Id; // Example of using a related record Id
    String hash = system.hashCode(f.Owner.Name + String.valueOf(f.createdDate)); // Example of using a generated hash

    if (!insertMap.containsKey(hash)) {
        // Create a new record if it doesn't exist
        // before attempting to modify it
        insertMap.put(
            hash,
            new Bar__c();
        );
    }

    // The record should always exist here so go ahead and
    // modify it
    insertMap.get(hash).Field__c = 'value';
}

if (!insertMap.isEmpty()) {
    insert insertMap.values();
}

Doing things this way helps ensure that you can de-dup without adding the SObject to a set which has many issues as other's have pointed out. Instead you're de-duping based on your key. This pattern has served me very well and typically requires less code then some of the other approaches mentioned.

0

You can use Set. Set represents a collection of unique elements with no duplicate values.

For more details visit https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_set.htm

  • 4
    This is an appropriate answer to the question for the details given, I don't know why you got down voted – EricSSH May 14 '18 at 19:26
  • 2
    Using a Set of any non-primitive is brittle and likely will not help the OP without a very careful implementation. Just posting a link to the documentation for Set doesn't really come close to enough information to make this answer a good one in my opinion. – Adrian Larson May 14 '18 at 20:51
0

You have to wrap your Object inside a wrapper apex class.

That wrapper class will override equals and hashcode methods and thus you can create a Set of WrapperClass.

Suppose your Object is Account: Your wrapper will be

public class AccountWrapper {
    Account instAcc;

    public AccountWrapper(Account acc) {
       this.instAcc = acc;
    }

    public Boolean equals(Object obj) {
        if (obj instanceof AccountWrapper) {
            AccountWrapper p = (AccountWrapper)obj;
            return ((instAcc.RecordTypeId==p.RecordTypeId) && (instAcc.TextField==p.TextField));
        }
        return false;
    }

    public Integer hashCode() {
        return System.hashCode(instAcc.Id);;
    }
}

And then in your Service class

// Create a set
Set<AccountWrapper> s1 = new Set<AccountWrapper>();

Account a1= Account(TextField='H1',RecordTypeId='P1');
insert a1;

Account a2= Account(TextField='H2',RecordTypeId='P2');
insert a2;

Account a3= Account(TextField='H2',RecordTypeId='P2');
insert a3;





s1.add(new AccountWrapper(a1));
s1.add(new AccountWrapper(a2));
s1.add(new AccountWrapper(a3));

System.assertEquals(2, s1.size());


// Verify that we have only two elements
// since the a2 is equal to a3.
System.assertEquals(2, s1.size());

You can read more about it here: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections_maps_keys_userdefined.htm

Note : If you are not doing DML beforeHand, your implementation of hashCode won't consider Id for generating Hashing.

  • Could this not be more generic so that you don't have to write a wrapper class for each object? Also, the OP specifically states he's trying to de-dup for an insert which means there will never be an Id and you only very briefly discuss how to handle it in that situation. You might want to consider covering that more in depth. – gNerb May 14 '18 at 22:27

Not the answer you're looking for? Browse other questions tagged or ask your own question.