6

Previously I had posted this question awhile back: Utility Methods flexible enough for all sObject Types

Now I want to expand my method a bit to return a Set<Object> instead of a List<Object>

Here is the original method:

public static List<Object> getValuesByField (Type listType, List<SObject> records, SObjectField field){
    List<Object> results = (Object[])listType.newInstance();
    for(SObject record: records) {
        results.add(record.get(field));
    }
    return results;
}

I use this method like so: (works just fine)

List<String> zip_codes = (List<String>) Utils.getValuesByField(List<String>.class, accounts, Account.ShippingPostalCode);

But now if I need a Set (unique list) I have this overloaded method:

public static Set<Object> getValuesByField (Type listType, List<SObject> records, SObjectField field, Boolean isSet){
    List<Object> result_list = (Object[])listType.newInstance();
    for(SObject record: records) {
        result_list.add(record.get(field));
    }
    Set<Object> results = new Set<Object>(result_list);
    return results;
}

And attempt to use it like so:

Set<String> zip_codes = (Set<String>) Utils.getValuesByField(Set<String>.class, accounts, Account.ShippingPostalCode, true);

I get the following error:

Incompatible types since an instance of Set<Object> is never an instance of Set<String>

Any thoughts?

1
  • 2
    My interpretation is that in place of a mechanism designed to allow code to be written ones for many types - e.g. generics in Java - a few special cases have been allowed in Apex for lists but these aren't a general feature hence the problem you have run into. So it looks like for sets you will always have to use explicit types e.g. Set<String> zip_codes = new Set<String>((List<String>) Utils.getValuesByField(...));. Bottom line is limit your ambitions when using Apex.
    – Keith C
    Aug 21, 2015 at 23:20

2 Answers 2

4

There's all sorts of dodgy ways to do this, but I find that the easiest is to simply use an "out-parameter," because then you don't even need to worry about casting. It works like this:

public static void getValuesByField (Set<Object> results, List<SObject> records, SObjectField field){
    for(SObject record: records) {
        results.add(record.get(field));
    }
}

Since objects always pass-by-reference, all you need to do is make sure your set is initialized beforehand.

Edit:

In fact, this is similar to a function that's included in my (soon-to-be-released) OSS library I'm writing:

//////////////////////////////////////////////////////////////////////
//  Get many values from many records at once
public static void get(SObject[] sources, SObjectField field, Object[] values) {
    for(SObject source: sources) {
        values.add(source.get(field));
    }
}

It's still not "developed" enough to be released yet, though. I'll announce it on more formal channels when it is.

Edit #2 (from comments)

You can always cast from an Object to a Set (assuming the rules are met), so you can, for example:

public static Object getValuesByField (Type resultType, List<SObject> records, SObjectField field, Boolean isSet) {
    Set<Object> results = new Set<Object>();
    for(SObject record: records) {
        results.add(record.get(field));
    }
    return JSON.deserialize(JSON.serialize(results), resultType);
}
7
  • Sad to have to do that but good to know it's a possibility.
    – Keith C
    Aug 21, 2015 at 23:29
  • 1
    I have a couple instances where I do this exact thing, but for this method the casting isn't a big deal, I like being able to use it with one line. Can you also show what would be the other way without the "out-parameter" Aug 21, 2015 at 23:31
  • @Xtremefaith Behold. Hacks at their best.
    – sfdcfox
    Aug 21, 2015 at 23:41
  • shouldn't the out parameter in the first example be named result_set or maybe, just results ? (as type of parameter is a set, not a list)
    – cropredy
    Aug 22, 2016 at 23:31
  • 1
    actually, I'm not sure your example 1 even is callable as sets of primitive types are never set<Object> - at least as per compile errors I get
    – cropredy
    Aug 23, 2016 at 15:13
1

You may be reinventing the wheel. I believe what you're looking for already exists in the SetUtils Class of the ApexLang Repo on GitHub. I think the pluckString methods may be along the lines of what you're looking for. See the code below.

/* ============================================================
 * This code is part of the "apex-lang" open source project avaiable at:
 * 
 *      http://code.google.com/p/apex-lang/
 *
 * This code is licensed under the Apache License, Version 2.0.  You may obtain a 
 * copy of the License at:
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * ============================================================
 */
global class SetUtils {

    global static Set<Blob> objectToBlob(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Blob> blobSet = new Set<Blob>();
        for(Object anObject : objectSet){
            if(anObject instanceof Blob){
                blobSet.add((Blob)anObject);
            }
        }
        return blobSet;
    }

    global static Set<Boolean> objectToBoolean(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Boolean> booleanSet = new Set<Boolean>();
        for(Object anObject : objectSet){
            if(anObject instanceof Boolean){
                booleanSet.add((Boolean)anObject);
            }
        }
        return booleanSet;
    }

    global static Set<Date> objectToDate(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Date> dateSet = new Set<Date>();
        for(Object anObject : objectSet){
            if(anObject instanceof Date){
                dateSet.add((Date)anObject);
            }
        }return dateSet;
    }

    global static Set<Datetime> objectToDatetime(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Datetime> datetimeSet = new Set<Datetime>();
        for(Object anObject : objectSet){
            if(anObject instanceof Datetime){
                datetimeSet.add((Datetime)anObject);
            }
        }
        return datetimeSet;
    }

    global static Set<Decimal> objectToDecimal(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Decimal> decimalSet = new Set<Decimal>();
        for(Object anObject : objectSet){
            if(anObject instanceof Decimal){
                decimalSet.add((Decimal)anObject);
            }
        }
        return decimalSet;
    }

    global static Set<Double> objectToDouble(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Double> doubleSet = new Set<Double>();
        for(Object anObject : objectSet){
            if(anObject instanceof Double){
                doubleSet.add((Double)anObject);
            }
        }return doubleSet;
    }

    global static Set<ID> objectToID(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<ID> idSet = new Set<ID>();
        for(Object anObject : objectSet){
            if(anObject instanceof ID){
                idSet.add((ID)anObject);
            }
        }
        return idSet;
    }

    global static Set<Integer> objectToInteger(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Integer> integerSet = new Set<Integer>();
        for(Object anObject : objectSet){
            if(anObject instanceof Integer){
                integerSet.add((Integer)anObject);
            }
        }
        return integerSet;
    }

    global static Set<Long> objectToLong(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Long> longSet = new Set<Long>();
        for(Object anObject : objectSet){
            if(anObject instanceof Long){
                longSet.add((Long)anObject);
            }
        }
        return longSet;
    }

    global static Set<Time> objectToTime(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<Time> timeSet = new Set<Time>();
        for(Object anObject : objectSet){
            if(anObject instanceof Time){
                timeSet.add((Time)anObject);
            }
        }
        return timeSet;
    }

    global static Set<String> objectToString(Set<Object> objectSet){
        if(objectSet == null){
            return null;    
        }
        Set<String> stringSet = new Set<String>();
        for(Object anObject : objectSet){
            if(anObject instanceof String){
                stringSet.add((String)anObject);
            }
        }
        return stringSet;
    }

    global static Set<Object> blobToObject(Set<Blob> blobSet){
        if(blobSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Blob aBlob : blobSet){
            objectSet.add(aBlob);
        }
        return objectSet;
    }

    global static Set<Object> booleanToObject(Set<Boolean> booleanSet){
        if(booleanSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Boolean aBoolean : booleanSet){
            objectSet.add(aBoolean);
        }
        return objectSet;
    }

    global static Set<Object> dateToObject(Set<Date> dateSet){
        if(dateSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Date aDate : dateSet){
            objectSet.add(aDate);
        }
        return objectSet;
    }

    global static Set<Object> datetimeToObject(Set<Datetime> datetimeSet){
        if(datetimeSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Datetime aDatetime : datetimeSet){
            objectSet.add(aDatetime);
        }
        return objectSet;
    }

    global static Set<Object> decimalToObject(Set<Decimal> decimalSet){
        if(decimalSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Decimal aDecimal : decimalSet){
            objectSet.add(aDecimal);
        }
        return objectSet;
    }

    global static Set<Object> doubleToObject(Set<Double> doubleSet){
        if(doubleSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Double aDouble : doubleSet){
            objectSet.add(aDouble);
        }
        return objectSet;
    }

    global static Set<Object> idToObject(Set<ID> idSet){
        if(idSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(ID aID : idSet){
            objectSet.add(aID);
        }
        return objectSet;
    }

    global static Set<Object> integerToObject(Set<Integer> integerSet){
        if(integerSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Integer aInteger : integerSet){
            objectSet.add(aInteger);
        }
        return objectSet;
    }

    global static Set<Object> longToObject(Set<Long> longSet){
        if(longSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Long aLong : longSet){
            objectSet.add(aLong);
        }
        return objectSet;
    }

    global static Set<Object> timeToObject(Set<Time> timeSet){
        if(timeSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(Time aTime : timeSet){
            objectSet.add(aTime);
        }
        return objectSet;
    }

    global static Set<Object> stringToObject(Set<String> stringSet){
        if(stringSet == null){
            return null;    
        }
        Set<Object> objectSet = new Set<Object>();
        for(String aString : stringSet){
            objectSet.add(aString);
        }
        return objectSet;
    }

    global static String toString(Set<Blob> blobSet){ return toString(blobToObject(blobSet)); }
    global static String toString(Set<Boolean> booleanSet){ return toString(booleanToObject(booleanSet)); }
    global static String toString(Set<Date> dateSet){ return toString(dateToObject(dateSet)); }
    global static String toString(Set<Datetime> datetimeSet){ return toString(datetimeToObject(datetimeSet)); }
    global static String toString(Set<Decimal> decimalSet){ return toString(decimalToObject(decimalSet)); }
    global static String toString(Set<Double> doubleSet){ return toString(doubleToObject(doubleSet)); }
    global static String toString(Set<ID> idSet){ return toString(idToObject(idSet)); }
    global static String toString(Set<Integer> integerSet){ return toString(integerToObject(integerSet)); }
    global static String toString(Set<Long> longSet){ return toString(longToObject(longSet)); }
    global static String toString(Set<Time> timeSet){ return toString(timeToObject(timeSet)); }
    global static String toString(Set<String> stringSet){ return toString(stringToObject(stringSet)); }

    global static String toString(Set<Object> objectSet){
        if(objectSet == null){
            return 'null';
        }
        if(objectSet.size() <= 0){
            return '{}';
        }
        String returnValue = '';
        Boolean isFirst = true;
        for(Object anObject : objectSet){
            if(isFirst){
                isFirst = false;    
            } else {
                returnValue += ',';
            }
            if(anObject instanceof Blob){ returnValue += '\'' + ((Blob)anObject).toString() + '\'';
            } else if(anObject instanceof Boolean){ returnValue += ((Boolean)anObject);
            } else if(anObject instanceof Date){ returnValue += '\'' + ((Date)anObject) + '\'';
            } else if(anObject instanceof Datetime){ returnValue += '\'' + ((Datetime)anObject) + '\'';
            } else if(anObject instanceof Integer){ returnValue += ((Integer)anObject);
            } else if(anObject instanceof Long){ returnValue += ((Long)anObject);
            } else if(anObject instanceof Decimal){ returnValue += ((Decimal)anObject);
            //} else if(anObject instanceof Double){ returnValue += ((Double)anObject);
            } else if(anObject instanceof String){ returnValue += '\'' + ((String)anObject) + '\'';
            //} else if(anObject instanceof ID){ returnValue += '\'' + ((ID)anObject) + '\'';
            } else if(anObject instanceof Time){ returnValue += '\'' + ((Time)anObject) + '\'';}
        }
        return '{' + returnValue + '}';
    }


    global static void assertEquals(Set<Blob> b1,Set<Blob> b2){ assertEquals(blobToObject(b1),blobToObject(b2)); }
    global static void assertEquals(Set<Boolean> b1,Set<Boolean> b2){ assertEquals(booleanToObject(b1),booleanToObject(b2)); }
    global static void assertEquals(Set<Date> d1,Set<Date> d2){ assertEquals(dateToObject(d1),dateToObject(d2)); }
    global static void assertEquals(Set<Datetime> d1,Set<Datetime> d2){ assertEquals(datetimeToObject(d1),datetimeToObject(d2)); }
    global static void assertEquals(Set<Decimal> d1,Set<Decimal> d2){ assertEquals(decimalToObject(d1),decimalToObject(d2)); }
    global static void assertEquals(Set<Double> d1,Set<Double> d2){ assertEquals(doubleToObject(d1),doubleToObject(d2)); }
    global static void assertEquals(Set<Integer> i1,Set<Integer> i2){ assertEquals(integerToObject(i1),integerToObject(i2)); }
    global static void assertEquals(Set<Long> l1,Set<Long> l2){ assertEquals(longToObject(l1),longToObject(l2)); }
    global static void assertEquals(Set<String> s1,Set<String> s2){ assertEquals(stringToObject(s1),stringToObject(s2)); }
    global static void assertEquals(Set<Time> t1,Set<Time> t2){ assertEquals(timeToObject(t1),timeToObject(t2)); }

    global static void assertEquals(Set<Object> expected, Set<Object> actual){
        System.assert(
            equals(expected,actual),
            'Assertion failed, the following two arrays are not equal.  Expected: ' 
                + toString(expected) 
                + ', Actual: ' 
                + toString(actual));
    }

    global static Boolean equals(Set<Blob> b1,Set<Blob> b2){ return equals(blobToObject(b1),blobToObject(b2)); }
    global static Boolean equals(Set<Boolean> b1,Set<Boolean> b2){ return equals(booleanToObject(b1),booleanToObject(b2)); }
    global static Boolean equals(Set<Date> d1,Set<Date> d2){ return equals(dateToObject(d1),dateToObject(d2)); }
    global static Boolean equals(Set<Datetime> d1,Set<Datetime> d2){ return equals(datetimeToObject(d1),datetimeToObject(d2)); }
    global static Boolean equals(Set<Decimal> d1,Set<Decimal> d2){ return equals(decimalToObject(d1),decimalToObject(d2)); }
    global static Boolean equals(Set<Double> d1,Set<Double> d2){ return equals(doubleToObject(d1),doubleToObject(d2)); }
    global static Boolean equals(Set<Integer> i1,Set<Integer> i2){ return equals(integerToObject(i1),integerToObject(i2)); }
    global static Boolean equals(Set<Long> l1,Set<Long> l2){ return equals(longToObject(l1),longToObject(l2)); }
    global static Boolean equals(Set<String> s1,Set<String> s2){ return equals(stringToObject(s1),stringToObject(s2)); }
    global static Boolean equals(Set<Time> t1,Set<Time> t2){ return equals(timeToObject(t1),timeToObject(t2)); }

    global static Boolean equals(Set<Object> expected, Set<Object> actual){
        if(expected == null && actual == null){
            return true;
        }
        if((expected == null && actual != null) || (expected != null && actual == null)){
            return false;
        }
        if(expected.size() != actual.size()){
            return false;
        }
        for(Object obj : expected){
            if(!actual.contains(obj)){
                return false;
            }
        }
        return true;
    }

    global static Set<Object> pluck(List<SObject> records, String fieldName){
        return pluck(listToSet(records),fieldName);
    }   

    global static Set<Object> pluck(Set<SObject> records, String fieldName){
        Set<Object> returnValue = null;
        if(records != null){
            returnValue = new Set<Object>();
            if(records.size() > 0 && StringUtils.isNotBlank(fieldName)){
                for(SObject record : records){
                    returnValue.add(record.get(fieldName));
                }
            }
        }
        return returnValue;
    }

    global static Set<String> pluckString(List<SObject> records, String fieldName){
        return pluckString(listToSet(records),fieldName);
    }

    global static Set<String> pluckString(Set<SObject> records, String fieldName){
        return objectToString(pluck(records,fieldName));
    }

    global static List<String> setToList(Set<String> aSet){
        List<String> returnValue = null;
        if(aSet != null){
            returnValue = new List<String>();
            if(aSet.size() > 0){
                for(String value : aSet){
                    returnValue.add(value);
                }
            }
        }
        return returnValue;
    }

    global static List<Object> setToList(Set<Object> records){
        List<Object> returnValue = null;
        if(records != null){
            returnValue = new List<Object>();
            if(records.size() > 0){
                for(Object record : records){
                    returnValue.add(record);
                }
            }
        }
        return returnValue;
    }

    global static List<SObject> setToList(Set<SObject> records){
        List<SObject> returnValue = null;
        if(records != null){
            returnValue = new List<SObject>();
            if(records.size() > 0){
                for(SObject record : records){
                    returnValue.add(record);
                }
            }
        }
        return returnValue;
    }

    global static Set<String> listToSet(List<String> aList){
        Set<String> returnValue = null;
        if(aList != null){
            returnValue = new Set<String>();
            if(aList.size() > 0){
                for(String value : aList){
                    returnValue.add(value);
                }
            }
        }
        return returnValue;
    }

    global static Set<Object> listToSet(List<Object> aList){
        Set<Object> returnValue = null;
        if(aList != null){
            returnValue = new Set<Object>();
            if(aList.size() > 0){
                for(Object value : aList){
                    returnValue.add(value);
                }
            }
        }
        return returnValue;
    }

    global static Set<SObject> listToSet(List<SObject> records){
        Set<SObject> returnValue = null;
        if(records != null){
            returnValue = new Set<SObject>();
            if(records.size() > 0){
                for(SObject record : records){
                    returnValue.add(record);
                }
            }
        }
        return returnValue;
    }

    global static Set<String> lowerCase(Set<String> strs){
        Set<String> returnValue = null;
        if(strs != null){
            returnValue = new Set<String>();
            if(strs.size() > 0){
                for(String str : strs){
                    returnValue.add(str == null ? null : str.toLowerCase());
                }
            }
        }
        return returnValue;
    }

    global static Set<String> upperCase(Set<String> strs){
        Set<String> returnValue = null;
        if(strs != null){
            returnValue = new Set<String>();
            if(strs.size() > 0){
                for(String str : strs){
                    returnValue.add(str == null ? null : str.toUpperCase());
                }
            }
        }
        return returnValue;
    }

    global static Set<String> trim(Set<String> strs){
        Set<String> returnValue = null;
        if(strs != null){
            returnValue = new Set<String>();
            if(strs.size() > 0){
                for(String str : strs){
                    returnValue.add(str == null ? null : str.trim());
                }
            }
        }
        return returnValue;
    }



}
2
  • well I'll be d**ned. I'd been using the sfdcfox version for a while - and apexlang has been around for a while no less.
    – cropredy
    Aug 22, 2016 at 23:21
  • @cropredy FYI, apexlang, while rather useful, is outdated, and doesn't take advantage of language features that are now native.
    – sfdcfox
    Aug 23, 2016 at 2:49

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .