SObjectCollection
SObjectCollection
is an eager collection of SObject
instances.
of
Constructs an SObjectCollection
with the provided Iterable<SObject>
.
Signature
static SObjectCollection of(Iterable<SObject> records)
Example
SObjectCollection.of([SELECT Id, Name, Amount FROM Opportunity WHERE Amount > 10000]);
SObjectCollection.of(Trigger.new);
isEmpty
Returns true if the collection contains no elements, false otherwise.
Signature
Boolean isEmpty()
Example
SObjectCollection.of(new List<SObject>()).isEmpty(); // true
SObjectCollection.of(new List<Opportunity>{new Opportunity()}).isEmpty(); // false
difference
Returns a collection view of those records that are not equal in the other
collection, considering only comparisonFields
in the comparison.
The record's ID is used to find the counterpart in the other
collection. Records without counterpart are included in the returned collection. An exception is thrown if there are multiple records with the same ID in the other
collection.
Signature
SObjectCollection difference(SObjectCollection other, Set<Schema.SObjectField> comparisonFields)
filter
Returns a SObjectCollection
view of records that satisfied predicate
.
Signature
SObjectCollection filter(SObjectPredicate predicate)
Two predicates are provided out of the box, FieldsMatch
and RecordMatch
. They are instantiated through factory methods on Match
.
Example
// List<Account> accounts = ...
SObjectCollection accountCollection = SObjectCollection.of(accounts);
Account prototype = new Account(Name = 'Foo');
SObjectCollection recordMatched = accountCollection.filter(Fn.Match.record(prototype));
SObjectCollection filtered = accountCollection.filter(Fn.Match.field(Account.Name).equals('Foo').also(Account.AnnualRevenue).greaterThan(100000));
remove
remove
works just like filter
, but records which match a predicate are removed from the collection view instead of kept.
find
Returns an OptionalSObject
wrapping the first record predicate
returns true
for, or an empty OptionalSObject
if no element is found.
Signature
OptionalSObject find(SObjectPredicate predicate)
forEach
Invokes provided SObjectFunction
on each element of the collection and returns the current collection.
Signature
SObjectCollection forEach(SObjectFunction fn)
Example
SObjectCollection.of(accounts).forEach(Fn.Debug);
pluck
pluckBooleans
Plucks Boolean
values at field
or relation
.
Signature
List<Boolean> pluckBooleans(Schema.SObjectField field)
List<Boolean> pluckBooleans(String relation)
pluckDates
Plucks Date
values at field
or relation
.
Signature
List<Date> pluckDates(Schema.SObjectField field)
List<Date> pluckDates(String relation)
pluckDatetimes
Plucks Datetime
values at field
or relation
.
Signature
List<Datetime> pluckDatetimes(Schema.SObjectField field)
List<Datetime> pluckDatetimes(String relation)
pluckDecimals
Plucks numerical values at field
or relation
.
Signature
List<Decimal> pluckDecimals(Schema.SObjectField field)
List<Decimal> pluckDecimals(String relation)
pluckIds
Plucks Id
values at field
or relation
or Id
field.
Signature
List<Id> pluckIds(Schema.SObjectField field)
List<Id> pluckIds(String relation)
List<Id> pluckIds()
pluckStrings
Plucks String
values at field
or relation
.
Signature
List<String> pluckStrings(Schema.SObjectField field)
List<String> pluckStrings(String relation)
Example
List<Opportunity> opportunities = new List<Opportunity>{
new Opportunity(Name = 'Opp1', Account = new Account(Name = 'Acc1')),
new Opportunity(Name = 'Opp2', Account = new Account(Name = 'Acc2'))
};
List<String> opportunityFieldNames = SObjectCollection.of(opportunities).pluckStrings(Opportunity.Name); // ['Opp1', 'Opp2']
List<String> opportunityRelationNames = SObjectCollection.of(opportunities).pluckStrings('Name'); // ['Opp1' 'Opp2']
List<String> accountRelationNames = SObjectCollection.of(opportunities).pluckStrings('Account.Name'); // ['Acc1', 'Acc2']
groupBy
Apex allows assignment of SObject
lists and sets to its “subclass”, and the other way around. An SObject
list is an instance of any SObject
“subclass” list!
List<SObject> objects = new List<SObject>();
System.debug(objects instanceof List<Account>); // true
System.debug(objects instanceof List<Opportunity>); // true
groupByT
methods return a raw Map<T, List<SObject>>
when listType
is not provided. This is more convenient and a cast is not required, but instanceof
can provide unexpected results.
Map<String, List<Account>> accountsByName = c.groupByStrings(Account.Name);
List<Account> fooAccounts = accountsByName.get('Foo');
List<SObject> objects = fooAccounts;
// since fooAccounts points to a returned list of SObjects, it can be anything!
System.assert(objects instanceof List<Opportunity>);
When listType
is provided, map values are properly typed lists, and there are no unexpected results with instanceof
.
accountsByName = c.groupByStrings(Account.Name, List<Account>.class);
fooAccounts = accountsByName.get('Foo');
objects = fooAccounts;
// this time around, it works fine!
System.assert(!(objects instanceof List<Opportunity>));
System.assert(objects instanceof List<Account>);
groupByBooleans
Groups records by Boolean
values at field
or apiFieldName
, with an optional strong listType
.
Signature
Map<Boolean, List<SObject>> groupByBooleans(String apiFieldName, Type listType)
Map<Boolean, List<SObject>> groupByBooleans(String apiFieldName)
Map<Boolean, List<SObject>> groupByBooleans(Schema.SObjectField field, Type listType)
Map<Boolean, List<SObject>> groupByBooleans(Schema.SObjectField field)
groupByDates
Groups records by Date
values at field
or apiFieldName
, with an optional strong listType
.
Signature
Map<Date, List<SObject>> groupByDates(String apiFieldName, Type listType)
Map<Date, List<SObject>> groupByDates(String apiFieldName)
Map<Date, List<SObject>> groupByDates(Schema.SObjectField field, Type listType)
Map<Date, List<SObject>> groupByBooleans(Schema.SObjectField field)
groupByDatetimes
Groups records by Datetime
values at field
or apiFieldName
, with an optional strong listType
.
Signature
Map<Datetime, List<SObject>> groupByDatetimes(String apiFieldName, Type listType)
Map<Datetime, List<SObject>> groupByDatetimes(String apiFieldName)
Map<Datetime, List<SObject>> groupByDatetimes(Schema.SObjectField field, Type listType)
Map<Datetime, List<SObject>> groupByDatetimes(Schema.SObjectField field)
groupByDecimals
Groups records by numeric values at field
or apiFieldName
, with an optional strong listType
.
Signature
Map<Decimal, List<SObject>> groupByDecimals(String apiFieldName, Type listType)
Map<Decimal, List<SObject>> groupByDecimals(String apiFieldName)
Map<Decimal, List<SObject>> groupByDecimals(Schema.SObjectField field, Type listType)
Map<Decimal, List<SObject>> groupByDecimals(Schema.SObjectField field)
groupByIds
Groups records by Id
values at field
or apiFieldName
, with an optional strong listType
.
Signature
Map<Id, List<SObject>> groupByIds(String apiFieldName, Type listType)
Map<Id, List<SObject>> groupByIds(String apiFieldName)
Map<Id, List<SObject>> groupByIds(Schema.SObjectField field, Type listType)
Map<Id, List<SObject>> groupByIds(Schema.SObjectField field)
groupByStrings
Groups records by String
values at field
or apiFieldName
, with an optional strong listType
.
Signature
Map<String, List<SObject>> groupByStrings(String apiFieldName, Type listType)
Map<String, List<SObject>> groupByStrings(String apiFieldName)
Map<String, List<SObject>> groupByStrings(Schema.SObjectField field, Type listType)
Map<String, List<SObject>> groupByStrings(Schema.SObjectField field)
pick
Returns a new SObjectCollection
view of the collection which keeps just the specified fields
, discarding others. Helps reduce overwriting potential for concurrent updates when locking is not an option.
Signature
SObjectCollection pick(List<Schema.SObjectField> fields)
SObjectCollection pick(Set<Schema.SObjectField> fields)
SObjectCollection pick(List<String> apiFieldNames)
SObjectCollection pick(Set<String> apiFieldNames)
Example
List<Opportunity> opportunities = new List<Opportunity>{
new Opportunity(Name = 'Foo', Amount = 10000, Description = 'Bar')
}
// Picked contains just Name and Amount fields. Description is not present.
SObjectCollection picked = SObjectCollection.of(opportunities).pick(new Set<String>{'Name', 'Amount'});
mapAll
Maps all elements of SObjectCollection
view into another SObjectCollection
or ObjectCollection
view with the provided function fn
.
For large data volumes, consider streaming implementations or custom written functions instead. Custom written functions can be over 10x faster than those generated by the MapTo
factory.
Signature
SObjectCollection mapAll(SObjectToSObjectFunction fn)
ObjectCollection mapAll(ObjectToSObjectFunction fn)
Example
List<Task> followUpTasks = SObjectCollection.of(opps)
.mapAll(
Fn.MapTo(Task.SObjectType)
.mapField(Task.WhatId, Opportunity.Id)
.setField(Task.Subject, 'Follow up')
.setField(Task.ActivityDate, Date.today())
).asList();
List<OpportunityAction> actions = (List<OpportunityAction>) SObjectCollection.of(opps)
.mapAll(
Fn.MapTo(OpportunityAction.class)
.mapField('oppId', Opportunity.Id)
.setField('action', 'follow up')
.setField('createdAt', Datetime.now())
).asList(List<OpportunityAction>.class);
private class MapOpportunityToOpportunityAction implements SObjectToObjectFunction {
public Object call(SObject obj) {
Opportunity opp = (Opportunity) obj;
OpportunityAction action = new OpportunityAction();
action.oppId = opp.Id;
action.action = 'follow up';
action.createdAt = Datetime.now();
return action;
}
}
List<OpportunityAction> actions = (List<OpportunityAction>) SObjectCollection.of(opps)
.mapAll(new MapOpportunityToOpportunityAction())
.asList(List<OpportunityAction>.class);
mapSome
Returns a new SObjectCollection
view formed by mapping those view elements that satisfy predicate
with function fn
, and keeping those that do not unchanged.
Signature
SObjectCollection mapSome(SObjectPredicate predicate, SObjectToSObjectFunction fn)
Example
private class DoubleAmount implements SObjectToSObjectFunction {
public SObject call(SObject record) {
record.put('Amount', 2 * (Decimal) record.get('Amount'));
return record;
}
}
List<Opportunity> opps = new List<Opportunity>{
new Opportunity(Amount = 100),
new Opportunity(Amount = 150)
};
SObjectCollection.of(opps).mapSome(Fn.Match.field('Amount').gt(120), new DoubleAmount()); // 100 remains, but 150 has been doubled to 300
mapTo
mapToDecimal
Maps a numeric field at field
or relation
to a DecimalCollection
. This is similar to pluckDecimals
, but unlike a raw List<Decimal>
returns a DecimalCollection
which provides further functions.
Signature
DecimalCollection mapToDecimal(Schema.SObjectField field)
DecimalCollection mapToDecimal(String relation)
mapToDouble
Maps a numeric field at field
or relation
to a DoubleCollection
. This is similar to pluckDoubles
, but unlike a raw List<Double>
returns a DoubleCollection
which provides further functions.
Signature
DoubleCollection mapToDouble(Schema.SObjectField field)
DoubleCollection mapToDouble(String relation)
Example
List<Opportunity> opps = new List<Opportunity>{
new Opportunity(Amount = 100),
new Opportunity(Amount = 150)
};
Double average = SObjectCollection.of(opps).mapToDouble(Opportunity.Amount).average();
asList
Returns a List
of records in the collection, either as a raw List<SObject>
, or as a List<T>
where T
is a “subclass“ of SObject
.
Apex allows assignment of SObject
lists and sets to its “subclass”, and the other way around:
List<SObject> objects = new List<SObject>();
List<Account> accounts = objects; // compiles!
List<Account> accounts = new List<Account>();
List<SObject> objects = accounts; // compiles as well!
An SObject
list is an instance of any SObject
“subclass” list!
List<SObject> objects = new List<SObject>();
System.debug(objects instanceof List<Account>); // true
System.debug(objects instanceof List<Opportunity>); // true
asList()
on SObjectCollection
returns a raw List<SObject>
. This is more convenient because the type does not need to be provided, and a cast is not required in either case, but instanceof
can provide unexpected results.
A concrete type of the list can be passed in as well. When this is done, the returned List
is of the correct concrete type instead of generic SObject
collection type:
List<Account> filteredAccounts = accountCollection.asList();
// List<SObject> returned!
List<Account> filteredAccounts = accountCollection.asList(List<Account>.class);
// List<Account> returned!
Signature
List<SObject> asList()
List<SObject> asList(Type listType)
Example
List<Opportunity> largeOpportunities = SObjectCollection.of(opportunities).asList(); // works, but instanceof can provide unexpected results
List<Opportunity> largeOpportunities = SObjectCollection.of(opportunities).asList(List<Opportunity>.class); // always works
asMap
Returns a grouping of records by their Id
s, either as a raw Map<Id, SObject>
, or as a Map<Id, List<T>>
, where T
is a “subclasses“ of SObject
.
We can assign to a raw Map<Id, SObject>
directly:
Map<Id, SObject> recordMap = SObjectCollection.of(accounts).asMap(); // Works!
However, to assign to a Map<Id, T>
, where T
is a “subclass“ of SObject
, we have to both cast and provide the correct concrete mapType
.
Map<Id, Account> recordMap = (Map<Id, Account>) SObjectCollection.of(accounts).asMap(Map<Id, Account>.class);
That’s because unlike List<SObject>
and Set<SObject>
which can be assigned to a List<T>
and a Set<T>
respectively, a Map<Id, SObject>
cannot be directly assigned to a Map<Id, T>
, where T
is a “subclass“ of SObject
.
List<Account> accountList = SObjectCollection.of(accounts).asList() // works!
Set<Account> accountSet = SObjectCollection.of(accounts).asSet() // works!
Map<Id, Account> accountsById = SObjectCollection.of(accounts).asMap() // DOES NOT WORK!!!
Signature
Map<Id, SObject> asMap()
Map<Id, SObject> asMap(Type mapType)