Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data sum of all instances attribute

Tags:

ios

core-data

Using Core Data, I encountered a problem. I have an entity "Movement" with an attribute "amount". How do I make the sum of all the "amount" of all instances? I'd like to understand how to use NSExpressionDescription, but it's good enough NSSet.

like image 812
Vins Avatar asked Feb 11 '13 23:02

Vins


3 Answers

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

like image 145
e1985 Avatar answered Sep 29 '22 02:09

e1985


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

like image 45
whyceewhite Avatar answered Sep 29 '22 01:09

whyceewhite


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.

like image 44
Yaman Avatar answered Sep 29 '22 00:09

Yaman