How often does it happen that you’d just need to extract one field from a collection, record by record, value by value, and pack the results into a separate list? And how many times of it do you repeat, copy-paste, or even create the same old lines of code from scratch, thinking there must be a better solution out there?
Well, there is now. Salesforce just might have forgotten about it in their Apex Recipes CollectionUtils. which enables you to extract basically anything from a given list of sObjects, except for a simple list of field values.
We’re happy to help out with the generateStringListFromSObjectList() method, for which you’ll need the following 4 parameters:
- List<sObject> listToExtractFrom
This is your list of records – it doesn’t have to be explicitly converted into a List<sObject> before. You can as well pass a List<Account> or List<CustomObject__c>. - String fieldAPIName
The added value here is that fieldAPIName can contain a relational field that’s one or multiple levels away, it just needs to be queried in the List<sObject> that’s passed to the method. That field can be denoted as you’d reference it in Apex or SOQL – if you pass a List<Contact> for example, you can extract ‘Account.Name’. - Boolean blankCheck
If true, nulls are excluded from the result, otherwise included. - Boolean uniqueCheck
If true, repetitive values will only be returned once, otherwise as often as they occur.
Now here’s our little gem, ready to be distributed across the globe:
public without sharing class CollectionUtils {
/* { Apex Recipes Code here } */
public static List<String> generateStringListFromSObjectList(List<sObject> listToExtractFrom, String fieldAPIName, Boolean blankCheck, Boolean uniqueCheck) {
List<String> listToReturn = new List<String>();
if(String.isNotBlank(fieldAPIName)){
List<String> fieldNameSplit = fieldAPIName.split('\\.');
for(SObject mySObject : listToExtractFrom){
// Retrieving Field Name
String fieldValueFound;
Integer fieldNameSplitSize = fieldNameSplit.size();
if(fieldNameSplitSize > 1){
// Multiple elements: Work down the cascade
sObject auxiliarySObject;
for(Integer i = 0; i < fieldNameSplitSize; i++){
// First element: Initiate officeFieldNameFound
if(i == 0){
auxiliarySObject = mySObject?.getSObject(fieldNameSplit[i]);
// Neither first nor last element: Retrieve the next SObject level
} else if(i > 0 && i < fieldNameSplitSize - 1){
auxiliarySObject = auxiliarySObject?.getSObject(fieldNameSplit[i]);
// Last Element: Get the field
} else if(i == fieldNameSplitSize - 1) {
fieldValueFound = (String) auxiliarySObject?.get(fieldNameSplit[i]);
}
}
} else {
fieldValueFound = (String) mySObject?.get(fieldNameSplit[0]);
}
// Either we do not do a blank check, or we do a blank check and the string to be checked is not blank
if(!blankCheck || (blankCheck && String.isNotBlank(fieldValueFound))){
// Either we do not do an unique check, or we do an unique check and the value is unique
if(!uniqueCheck || (uniqueCheck && !listToReturn.contains(fieldValueFound))){
listToReturn.add(String.valueOf(fieldValueFound));
}
}
}
}
return listToReturn;
}
}