Core Data sum of all instances attribute

Here's a Swift example of summing a managed object's attribute using NSExpression and NSExpressionDescription. The example assumes that the managed object is named Movement and the attribute to sum is amount.

Example:

 func sumAmount() -> Double {
    var amountTotal : Double = 0

    // Step 1:
    // - Create the summing expression on the amount attribute.
    // - Name the expression result as 'amountTotal'.
    // - Assign the expression result data type as a Double.

    let expression = NSExpressionDescription()
    expression.expression =  NSExpression(forFunction: "sum:", arguments:[NSExpression(forKeyPath: "amount")])
    expression.name = "amountTotal";
    expression.expressionResultType = NSAttributeType.doubleAttributeType

    // Step 2:
    // - Create the fetch request for the Movement entity.
    // - Indicate that the fetched properties are those that were
    //   described in `expression`.
    // - Indicate that the result type is a dictionary.

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Movement")
    fetchRequest.propertiesToFetch = [expression]
    fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType

    // Step 3:
    // - Execute the fetch request which returns an array.
    // - There will only be one result. Get the first array
    //   element and assign to 'resultMap'.
    // - The summed amount value is in the dictionary as
    //   'amountTotal'. This will be summed value.

    do {
        let results = try context.fetch(fetchRequest)
        let resultMap = results[0] as! [String:Double]
        amountTotal = resultMap["amountTotal"]!
    } catch let error as NSError {
        NSLog("Error when summing amounts: \(error.localizedDescription)")
    }

    return amountTotal
}

Additional Discussion of Steps:

Step 1 - Create an NSExpressionDescription variable. This NSExpressionDescription indicates what type of function is applied to the arguments. The sum function is being applied to the amount attribute.

expression.expression =  NSExpression(forFunction: "sum:", 
    arguments:[NSExpression(forKeyPath: "amount")])

Notice that the arguments parameter is an array. You can pass different types of expressions in the array, but, in our case we only want the amount attribute.

Step 2 - The fetch request is established here. Notice that the result type is specified as a dictionary: fetchRequest.resultType = NSAttributeType.DictionaryResultType. In Step 3 we will use the expression.name value of amountTotal as the key to access the summed value.

Step 3 - We execute the fetch request and returned will be a one element array. The element will be a dictionary which we caste to [String:Double]: let resultMap = results[0] as! [String:Double]. The dictionary's value is a Double because in Step 1 we indicated that the expression.expressionResultType would be a Double.

Finally, we access the sum by calling the dictionary value associated with the amountTotal key: resultMap["amountTotal"]!


References:

NSExpression and NSExpressionDescription


Having a managedObjectContext:

NSManagedObjectContext *managedObjectContext = ...

We create a fetch request with return type dictionary:

NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass([Movement class])];
fetchRequest.resultType = NSDictionaryResultType;

Then we create an expression description to calculate the sum:

NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
expressionDescription.name = @"sumOfAmounts";
expressionDescription.expression = [NSExpression expressionForKeyPath:@"@sum.amount"];
expressionDescription.expressionResultType = NSDecimalAttributeType;

Set the request properties to fetch:

fetchRequest.propertiesToFetch = @[expressionDescription];

We could also set a predicate if we want.

And last we execute the request and get an array containing a dictionary with one key(@"sumOfAmounts") and its value is a NSNumber with the sum of amounts.

NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (result == nil)
{
    NSLog(@"Error: %@", error);
}
else
{
    NSNumber *sumOfAmounts = [[result objectAtIndex:0] objectForKey:@"sumOfAmounts"];
}

Cheers


The best way is to use a fetch for specific values and supply a NSExpressionDescription with a sum: function.

When you execute the fetch you get a one element array containing a dictionary whose keys match the expression descriptions and whose values are the results of the expressions. In this case, you would get a sum key whose value would be the sum of the attributes given the expression.

Tags:

Ios

Core Data