What is a use case where one would use an SObject as a Map key?

I use SObject as a key when I want to use a composite key. For example, say I want to find duplicates based on first name, last name, and email. I might loop through a list of contacts, and create a contact that matches the keys I'm using:

Incoming.put(new Contact(FirstName= record.FirstName, LastName= record.LastName, Email= record.Email), record);

Then, when I query the database on those three fields, I can quickly look up the new records, again by constructing a key from an SObject.

This is more reliable than concatenating strings and hoping that it is unique enough that it won't grab false positives.


Example:

trigger matchDupes on Lead (before insert, before update) {
    Map<Lead, Lead> leads = new Map<Lead, Lead>();
    Set<String> company = new Set<String>(), email = new Set<String>();

    // Match duplicates in this transaction
    for(Lead record: Trigger.new) {
        Lead key = new Lead(Company=record.Company, Email=record.Email);
        if(leads.containsKey(key)) {
            record.addError('This lead matches a duplicate lead.');
            continue;
        }
        company.add(record.company);
        email.add(record.email);
        leads.put(key, record);
    }
    // Match duplicates in the database
    for(Lead record: [SELECT Email, Company from Lead WHERE Company in :company and Email IN :email]) {
        Lead key = new Lead(Company=record.Company, Email=record.Email),
             dupLead = leads.remove(key);
        if(dupLead != null) {
           dupLead.addError('This lead matches a duplicate lead.');
        }
    }
}

Note that I could use an arbitrary number of fields (in most cases). There's several considerations, such as case sensitivity, not included here. I'd recommend always trying to convert to a common case when putting into/out of the map, but generally speaking this technique works well.


Sfdcfox explains a great reason to use this, but at a more abstract level the reason it's supported it because maps can have any type as a key.

In a model where custom classes, or any other type, can be used as keys there seems little reason to make a special case for forbidding SObjects.

Using an apex class as a map key has equally odd behavior (without implementing the equals and hashCode methods at least), so by this logic making SObject special seems inexplicable.


Here's another use-case:

I have an algo that creates Opps and Opportunity Lineitems , then rolls up the Opportunity Lineitems based on part product-code, then inserts Opps, then finally the OLIs.

// helper collection needed for roll-up routine
Map<OpportunityLineItem, String> opportunityLineItemToProductCodeMap = new Map<OpportunityLineItem, String>();

This is implemented with a deferred DML chain (for bulkification), so neither the OLIs nor the Opportunities have Ids until after the roll-up.

When I assign a part (using PricebookEntryId) to the Opportunity Lineitem, I found that I could not reference through to the part's Sku from the Oli object (because it's uncommitted), so I had to maintain a data structure that contains the Oli to product code relationship purely for the roll-up exercise.

Why I Think It's Okay In This Case:

Because 2 identical lineitems would map to the same product code, I don't need to worry about the case where identical field values result in an overwrite of the product code (it would be the same). I also guarantee that once the supporting data structure is in use, the keyed OLIs never change until after the data structure is utilized.

Note that this wouldn't be necessary if my requirement was to roll-up based on part-Id rather than product code. A part-Id based roll-up would require that product code is unique across all parts, and I have duplicates in my data set. Instead, I am able to roll-up on product code only because I can assert that all parts with duplicate product codes are interchangeable (from my stakeholder's perspective) and the price is not prescribed by what might be a variable list price, but rather by the opportunity line item unit price itself, which is being calculated independent of list price.

Alternatively...

However, the above is probably less readable and maintainable than a inner-class holding both the OLi and the product code String, which could then be stored together in a single list.

Similarly, one could use two lists and rely on the ordinality of lists to extract the elements from both while looping through only 1 (the OLI list).

Tags:

Map

Apex

Sobject