+ Start a Discussion
BrianWXBrianWX 

Using Maps

Hi,

 

I have this situation.

 

I have a mapping table call SourceDetail__c.  It has RecordTypeId__c, LeadSubType__c, SourceDetailName__c.  I try to read the entire table into a Map.  Say it's a small table with 1000 rows.

 

Map<Id, SourceDetail__c> mSourceDetail = NEW Map<Id, SourceDetail__c>([SELECT RecordTypeId__c, LeadSubtype__c, SourceDetailName__c FROM SourceDetail__c]);

 

Will the Map key contain the RecordTypeId__c value from the SourceDetail__c table?

 

I try to use the following codes to see if it contains the RecordTypeId__c key from the table, but it returns null.

 

Boolean contains = mSourceDetail.containsKey('012V00000008U7h');

System.assertEquals(contains, True);

 

If I cannot do this, may you suggest a solution for my situation?

 

When I import leads, I need to update Lead Subtype field with value from the SourceDetail__c table base on the inserted lead RecordTypeId and Source Detail value.  In other word the SourceDetail__c table contains all the mapping values to Lead Subtype with RecordTypeId and SourceDetailName being the matching keys.

 

How do I address this in a bulkifiable manner?

 

I was thinking read all the SourceDetail__c into a Map<Id, SourceDetail__c> where Id is the RecordTypeId from this mapping table.

 

Then in the FOR(Lead l : TRIGGER.NEW), I match with the Map.  But the Map seems not contain the RecordTypeId as I wanted.  What can I do differently?

 

Thanks,

Brian

 

bob_buzzardbob_buzzard

According to the Apex Docs, when you initialise a map from a list of sobjects (which is what your query returns) the map is keyed by the sobject id.  The id is implicit in the SOQL query so is returned whether you include it in the list of fields or not.  If you want to key your map by record id, you'll have to iterate the list of records returned from the query and put them into the map individually.

zachelrathzachelrath

Brian,

 

If I understand you correctly, what you're trying to do is to find the appropriate Lead Subtype to assign to a new Lead based on a "primary key" made up of RecordTypeId__c and SourceDetailName__c. So, what you really want is to put all of your SourceDetail__c records into a map keyed by this custom "primary key". Here's what I mean:

 

 

// Prepare a Map of SourceDetail__c records
// keyed by a custom "primary key":
// Primary Key = RecordTypeId__c + '/' + SourceDetailName__c ;
Map<String,SourceDetail__c> mSourceDetails 
   = new Map<String,SourceDetail__c>();

// Loop through all of your SourceDetail__c records
// and 'key' them into our map
for (List<SourceDetail__c> sourceDetails : 
       [SELECT RecordTypeId__c, LeadSubtype__c, SourceDetailName__c 
        FROM SourceDetail__c]) {
   for (SourceDetail__c sd : sourceDetails) {
       // Construct the primaryKey
       String primaryKey = sd.RecordTypeId__c + '/' + sd.SourceDetailName__c;
       // Add this SourceDetail__c record to our map, 
       //    keyed by its composite Primary Key
       mSourceDetails.put(primaryKey,sd);
   }
}

// NOW, you can do something like this:
for (Lead l : Trigger.New) {
   // Construct the Lead's 'Primary Key',
   // so that we can try to find a matching record in mSourceDetails
   String primaryKey = l.RecordTypeId + '/' + l.Lead_Source_Detail__c;
   if (mSourceDetails.containsKey(primaryKey)) {
      SourceDetail__c sd = mSourceDetails.get(primaryKey);
      // Populate the new Lead's 'Subtype' field
      //    using the matching Source Detail record's 'LeadSubtype__c' field
      l.Subtype = sd.LeadSubtype__c;
   }
}

 

To make this more efficient in a Trigger, you'd want to only grab those SourceDetail__c records that you actually need by doing an initial loop through the Leads in Trigger.new to see which Lead Record Types and Lead Source Details are actually being inserted. For example, here would be a complete Trigger implementation:

 

trigger Leads on Lead (before insert) {

	if (Trigger.isBefore && Trigger.isInsert) {
	
		Set<String> recordTypeIds = new Set<String>();
		Set<String> leadSourceDetailNames = new Set<String>();
		
		for (Lead l : Trigger.new) {
			if (!recordTypeIds.contains(l.RecordTypeId)) {
				recordTypeIds.add(l.RecordTypeId);
			}
			if (l.Lead_Source_Detail__c != NULL
				&& !leadSourceDetailNames.contains(l.Lead_Source_Detail__c)) {
				leadSourceDetailNames.add(l.Lead_Source_Detail__c);
			}	
		}
		
		// Prepare a Map of SourceDetail__c records
		// keyed by a custom "primary key":
		// Primary Key = RecordTypeId__c + '/' + SourceDetailName__c ;
		Map<String,SourceDetail__c> mSourceDetails 
		   = new Map<String,SourceDetail__c>();

		// Loop through all of your SourceDetail__c records
		// and 'key' them into our map
		for (List<SourceDetail__c> sourceDetails : 
			   [SELECT RecordTypeId__c, LeadSubtype__c, SourceDetailName__c 
				FROM SourceDetail__c
				WHERE RecordTypeId__c in :recordTypeIds
				AND SourceDetailName__c in :leadSourceDetailNames]) {
		   for (SourceDetail__c sd : sourceDetails) {
			   // Construct the primaryKey
			   String primaryKey = sd.RecordTypeId__c + '/' + sd.SourceDetailName__c;
			   // Add this SourceDetail__c record to our map, 
			   //    keyed by its composite Primary Key
			   mSourceDetails.put(primaryKey,sd);
		   }
		}
		
		if (!mSourceDetails.isEmpty()) {
		
			for (Lead l : Trigger.New) {
			
			   // Construct the Lead's 'Primary Key',
			   // so that we can try to find a matching record in mSourceDetails
			   String primaryKey = l.RecordTypeId + '/' + l.Lead_Source_Detail__c;
			   
			   if (mSourceDetails.containsKey(primaryKey)) {
			   
			      // Get the SourceDetail__c record with this Primary Key
				  SourceDetail__c sd = mSourceDetails.get(primaryKey);
				  
				  // Populate the new Lead's 'Subtype' field
				  //    using the matching Source Detail record's 'LeadSubtype__c' field
				  l.Subtype = sd.LeadSubtype__c;
				  
			   }
			}
		
		}
	
	} // end if (Trigger.isBefore && Trigger.isInsert)

} // end trigger

 

 

 

 

BrianWXBrianWX

Thank you all for your feedbacks.


The challenge in dealing with Map is because the SourceDetail__c requires matching two by two fields (RecordTypeId and SourceDetailName) to get LeadSubtype__c.  And Map is using sObject Id as pointed out by bob_buzzard (Thank you bob_buzzard).

 

I use approach similar to zachelrath's but without checking the needed SourceDetail__c and RecordTypeId.  That's idea is great.

 

1. Create an empty Map<String, String>mSourceDetail;

 

2. Create a List and read all records from SourceDetail__c object  into a List lstSourceDetail.

 

3. Loop through lstSourceDetail.

. Concatenate a Key = RecordTypeId + SourceDetailName;

. Put Key and LeadSubtype into Map<String, String> value pair

. Note:  now you have a unique value pair for matching when looping through trigger.

 

4. Now in your FOR (Lead l : TRIGGER.NEW)

. Concatenate lKey = l.RecordTypeId + l.SourceDetailName;

. Set l.LeadSubtype = mSourceDeteail.get(lKey);

 

5. Catch exception

 

It uses one single SOQL query and is bulkifable.  The draw back with this approach is there is a limit of 10000 records in SourceDetail__c object.  The codes are minimal too.  If SourceDetail__c object  grows more than 10K, we need to use a FOR loop to pull into the list lstSourceDetail.


Thank you,Brian