CommaToast
CommaToast

Reputation: 12238

How can I set up an NSPredicate to sum the values of properties inside dictionaries in an array of dictionaries in iOS?

Q: How can I set up an NSPredicate to sum the values of properties inside dictionaries in an array of dictionaries in iOS?

Note: answer must be a single string used in a format argument of an NSPredicate object. As in: var pred1:NSPredicate = NSPredicate.init(format: *ANSWER*)


Suppose I have the following dictionary:

var ticketState:Dictionary =
[
    "orderTotal":1.2,
    "payments":
    [
        [
            "authCode":"12345",
            "isVoid":"false",
            "amount":1.0
        ],
        [
            "authCode":"54321",
            "isVoid":"false",
            "amount":0.2
        ]
    ],
    "associatedWithReturn":false
]

Now I want an NSPredicate to check if all the amounts sum up to equal the order total.

Something like:

var cond1:String = "sum($payments[].amount == $orderTotal)"

Note: I cannot use array filtering or a second predicate.

I can access the first payment amount like this:

var cond1:String = "$payments[1].amount > 0" //works, returns true

This will return true. I just want to check that the sum of all the amounts in payments equals the order Total. This should be a simple operation defined by a single predicate.

How do I write a one-line NSPredicate declaration that accomplishes this?


Note: there is a very specific reason I need it to be a one-line, single NSPredicate initialization format string. But that's an implementation detail; don't worry about it, because someone else might have a different reason than me for needing the same type of initialization.

Upvotes: 1

Views: 1477

Answers (3)

CommaToast
CommaToast

Reputation: 12238

var cond1:String = "[email protected] == $orderTotal"

var pred1:NSPredicate = NSPredicate.init(format: cond1)
var rule1:GKRule = GKRule.init(predicate: pred1, assertingFact:"completeable", grade:1.0)
var ruleSys:GKRuleSystem = GKRuleSystem.init()

ruleSys.addRule(rule1)
ruleSys.state.addEntriesFromDictionary(ticketState)
ruleSys.evaluate()
print(ruleSys.facts)

Returns ["completeable"].

Thanks sage444 for the tip that led me to that.

To put it another way:

Swift:

var pred1:NSPredicate = 
    NSPredicate.init(format:  "[email protected] == $orderTotal")

Objective C:

NSPredicate *pred1 = 
    [[NSPredicate alloc]initWithFormat:@"[email protected] == $orderTotal"];

My purpose for this is to initialize an Apple GameplayKit GKRule object, which takes an NSPredicate object as an argument. However I think it could have many other applications.

Upvotes: 1

Mehul Thakkar
Mehul Thakkar

Reputation: 12594

Use following code to get sum

var totalAmount:Float = (ticketState.valueForKeyPath("[email protected]") as! NSNumber).floatValue;

Now you can compare it or do anything you want.

For other related operators like average,count etc. you can check other collection operators at following link:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html

Upvotes: 1

vadian
vadian

Reputation: 285160

This is a native Swift solution which uses to built-in functions

  • map to put the amount values into an array
  • reduce to add the values

let payments = ticketState["payments"] as! [[String:AnyObject]]
let amountSum = payments.map{$0["amount"] as! Double}.reduce(0, combine: {$0 + $1})

Upvotes: 1

Related Questions