Recently I needed a way to quickly filter out an array (i.e. list) of sObjects in Apex. Since I was a C# developer for 5 years, I immediately remembered how I used LINQ in C# for this purpose and I hoped there’s something similar in Apex as well. However, in Apex, there is no easy way to filter through a list without using the for
loop.
I’m usually trying to limit the number of DML statements in Apex, so I’d often query more records at once (if I know I’m not going to reach 50.000 records limit) and then filter through it via for
loop in case I need to get multiple sets of records from that query. Because of this need, I decided to create a helper class that will help me achieve this without writing the for
loop every time I need to filter something. I’m sharing the class with you below, and you can share your thoughts with me in the comments.
public class ArrayFilter {
public static List < sObject > filter ( List < sObject > arrayToFilter , List < ArrayFilter . Filter > filters , String filterLogic ){
List < sObject > retVal = new List < sObject >();
if ( arrayToFilter == null ){
return retVal ;
}
if ( filterLogic == ' OR ' ){
for ( Integer i = 0 ; i < arrayToFilter . size (); i ++){
for ( Integer j = 0 ; j < filters . size (); j ++){
Filter f = filters [ j ];
if ( ArrayFilter . Assert ( arrayToFilter [ i ], f )){
retVal . add ( arrayToFilter [ i ]);
continue ;
}
}
}
} else if ( filterLogic == ' AND ' ){
for ( Integer i = 0 ; i < arrayToFilter . size (); i ++){
Boolean passedTheFilter = true ;
for ( Integer j = 0 ; j < filters . size (); j ++){
Filter f = filters [ j ];
if ( ArrayFilter . Assert ( arrayToFilter [ i ], f ) == false ){
passedTheFilter = false ;
break ;
}
}
if ( passedTheFilter ){
retVal . add ( arrayToFilter [ i ]);
}
}
} else {
throw new ArrayFilterException ( ' Invalid filter logic "' + filterLogic + '" in class ArrayFilter . \ n \ nAvailable options are "OR" and "AND" ' );
}
return retVal ;
}
public static Boolean Assert ( sObject o , Filter f ){
if ( f . valueType == ' Boolean ' ){
if ( f . operator == ' != ' ){
return (( Boolean ) o . get ( f . fieldName )) != f . valBoolean ;
} else {
return (( Boolean ) o . get ( f . fieldName )) == f . valBoolean ;
}
} else if ( f . valueType == ' Date ' ){
if ( f . operator == ' != ' ){
return (( Date ) o . get ( f . fieldName )) != f . valDate ;
} else if ( f . operator == '>' ){
return (( Date ) o . get ( f . fieldName )) > f . valDate ;
} else if ( f . operator == '<' ){
return (( Date ) o . get ( f . fieldName )) < f . valDate ;
} else {
return (( Date ) o . get ( f . fieldName )) == f . valDate ;
}
} else if ( f . valueType == ' DateTime ' ){
if ( f . operator == ' != ' ){
return (( DateTime ) o . get ( f . fieldName )) != f . valDateTime ;
} else if ( f . operator == '>' ){
return (( DateTime ) o . get ( f . fieldName )) > f . valDateTime ;
} else if ( f . operator == '<' ){
return (( DateTime ) o . get ( f . fieldName )) < f . valDateTime ;
} else {
return (( DateTime ) o . get ( f . fieldName )) == f . valDateTime ;
}
} else if ( f . valueType == ' Time ' ){
if ( f . operator == ' != ' ){
return (( Time ) o . get ( f . fieldName )) != f . valTime ;
} else if ( f . operator == '>' ){
return (( Time ) o . get ( f . fieldName )) > f . valTime ;
} else if ( f . operator == '<' ){
return (( Time ) o . get ( f . fieldName )) < f . valTime ;
} else {
return (( Time ) o . get ( f . fieldName )) == f . valTime ;
}
} else if ( f . valueType == ' Integer ' ){
if ( f . operator == ' != ' ){
return (( Integer ) o . get ( f . fieldName )) != f . valInteger ;
} else if ( f . operator == '>' ){
return (( Integer ) o . get ( f . fieldName )) > f . valInteger ;
} else if ( f . operator == '<' ){
return (( Integer ) o . get ( f . fieldName )) < f . valInteger ;
} else {
return (( Integer ) o . get ( f . fieldName )) == f . valInteger ;
}
} else if ( f . valueType == ' Decimal ' ){
if ( f . operator == ' != ' ){
return (( Decimal ) o . get ( f . fieldName )) != f . valDecimal ;
} else if ( f . operator == '>' ){
return (( Decimal ) o . get ( f . fieldName )) > f . valDecimal ;
} else if ( f . operator == '<' ){
return (( Decimal ) o . get ( f . fieldName )) < f . valDecimal ;
} else {
return (( Decimal ) o . get ( f . fieldName )) == f . valDecimal ;
}
} else if ( f . valueType == ' Double ' ){
if ( f . operator == ' != ' ){
return (( Double ) o . get ( f . fieldName )) != f . valDouble ;
} else if ( f . operator == '>' ){
return (( Double ) o . get ( f . fieldName )) > f . valDouble ;
} else if ( f . operator == '<' ){
return (( Double ) o . get ( f . fieldName )) < f . valDouble ;
} else {
return (( Double ) o . get ( f . fieldName )) == f . valDouble ;
}
} else if ( f . valueType == ' Long ' ){
if ( f . operator == ' != ' ){
return (( Long ) o . get ( f . fieldName )) != f . valLong ;
} else if ( f . operator == '>' ){
return (( Long ) o . get ( f . fieldName )) > f . valLong ;
} else if ( f . operator == '<' ){
return (( Long ) o . get ( f . fieldName )) < f . valLong ;
} else {
return (( Long ) o . get ( f . fieldName )) == f . valLong ;
}
} else if ( f . valueType == ' Id ' ){
if ( f . operator == ' != ' ){
return (( Id ) o . get ( f . fieldName )) != f . valId ;
} else {
return (( Id ) o . get ( f . fieldName )) == f . valId ;
}
} else if ( f . valueType == ' String ' ){
if ( f . operator == ' != ' ){
return !(( String ) o . get ( f . fieldName )). equals ( f . valString );
} else if ( f . operator == ' CONTAINS ' ){
return (( String ) o . get ( f . fieldName )). contains ( f . valString );
} else if ( f . operator == ' STARTS ' ){
return (( String ) o . get ( f . fieldName )). startsWith ( f . valString );
} else if ( f . operator == ' ENDS ' ){
return (( String ) o . get ( f . fieldName )). endsWith ( f . valString );
} else {
return (( String ) o . get ( f . fieldName )). equals ( f . valString );
}
}
return false ;
}
public class Filter {
public String fieldName { get ; private set ;}
public String valueType { get ; private set ;}
public String operator { get ; private set ;} //>,<,=,!=, CONTAINS, STARTS, ENDS
public Boolean valBoolean { get ; private set ;}
public Date valDate { get ; private set ;}
public DateTime valDateTime { get ; private set ;}
public Time valTime { get ; private set ;}
public Integer valInteger { get ; private set ;}
public Decimal valDecimal { get ; private set ;}
public Double valDouble { get ; private set ;}
public Long valLong { get ; private set ;}
public Id valId { get ; private set ;}
public String valString { get ; private set ;}
public Filter ( String fieldName , String operator , Boolean value ){
this . fieldName = fieldName ;
this . valueType = ' Boolean ' ;
this . valBoolean = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Date value ){
this . fieldName = fieldName ;
this . valueType = ' Date ' ;
this . valDate = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , DateTime value ){
this . fieldName = fieldName ;
this . valueType = ' DateTime ' ;
this . valDateTime = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Time value ){
this . fieldName = fieldName ;
this . valueType = ' Time ' ;
this . valTime = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Integer value ){
this . fieldName = fieldName ;
this . valueType = ' Integer ' ;
this . valInteger = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Decimal value ){
this . fieldName = fieldName ;
this . valueType = ' Decimal ' ;
this . valDecimal = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Double value ){
this . fieldName = fieldName ;
this . valueType = ' Double ' ;
this . valDouble = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Long value ){
this . fieldName = fieldName ;
this . valueType = ' Long ' ;
this . valLong = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , Id value ){
this . fieldName = fieldName ;
this . valueType = ' Id ' ;
this . valId = value ;
this . operator = operator ;
}
public Filter ( String fieldName , String operator , String value ){
this . fieldName = fieldName ;
this . valueType = ' String ' ;
this . valString = value ;
this . operator = operator ;
}
}
}
Sample usage
Below you can see some of the ways you could use this ArrayFilter
class to filter a list of objects.
Example #1:
//filter by String
List < sObject > filteredArray = ArrayFilter . filter ( someArrayToFilter , new ArrayFilter . Filter []{ new ArrayFilter . Filter ( ' Name ' , '=' , ' John Doe ' )}, ' OR ' );
Example #2:
List < ArrayFilter . Filter > filters = new List < ArrayFilter . Filter >();
filters . add ( new ArrayFilter . Filter ( ' Migration_Start_Date__c ' , '=' , Date . newInstance ( 2000 , 1 , 2 )));
filters . add ( new ArrayFilter . Filter ( ' Migration_Start_Date__c ' , '>' , Date . newInstance ( 2010 , 3 , 4 )));
List < Account > filtered = ArrayFilter . filter ( someArrayToFilter , filters , ' OR ' );
Example #3:
//text supports operators: =, !=, CONTAINS, ENDS, STARTS
List < ArrayFilter . Filter > filters = new List < ArrayFilter . Filter >();
filters . add ( new ArrayFilter . Filter ( ' Phone ' , ' STARTS ' , ' 0355 ' ));
filters . add ( new ArrayFilter . Filter ( ' NAME ' , ' STARTS ' , ' Jo ' ));
List < Account > filtered = ArrayFilter . filter ( someArrayToFilter , filters , ' AND ' );