Collection operator for summing a NSDictionary's values - nspredicate

Is there a simple way using KVO and collection operators, that I can sum the total value of a NSDictionary like { ID: NSNumber }?
Example:
#{
"my_ID_abcd": #(8),
"my_ID_efgh": #(2),
"my_ID_ijkl": #(3)
}
Would give 13 as a result.
Indeed #count doesn't return what I want, and #sum doesn't work here.. allValues.#sum.self neither...
I need to check the sum in a NSPredicate...
Thanks!

A way to do that would be to use a SUBQUERY(,,) & FUNCTION(,):
NSDictionary *dict = #{ #"my_ID_abcd": #(8),
#"my_ID_efgh": #(2),
#"my_ID_ijkl": #(3)};
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(FUNCTION(SELF, 'allValues'), $idsValues, $idsValues = $idsValues).#sum.integerValue = %d", value];
BOOL pass = [predicate evaluateWithObject:dict];
From what I tried, you can't really chain two operators (ie #sum & #allValues, so I used a SUBQUERY:
SUBQUERY(allValues, $idsAllValues, $idsAllValues = $idsAllValues)
Since, you need a "true/false" on the last one, I used a simple comparaison against itself.
And I used FUNCTION to build the array of values.
That way, in the end, I can call #sum on it.
I'm not very familiar with SUBQUERY & FUNCTION, maybe the call could be simplified.
If you could use predicateWithBlock:, it would be much simpler though:
NSPredicate *withBlock = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSInteger sum = [[[evaluatedObject allValues] valueForKeyPath:#"#sum.self"] integerValue];
return sum == value;
}];

Related

Swift Core Data with for loop only saving last item in array

One Gymnast to Many Meets. Can someone provide some info on how I can set meet for each gymnast. It prints each gymnast in array but the meet only gets assigned to last gymnast in array.
newMeet.meetName = "Winter Classic"
newMeet.meetDate = "Sat October 26, 2016"
newMeet.meetDateSort = "2016-09-26"
newMeet.meetTime = "02:00 PM"
newMeet.meetLocation = "Phoenix, AZ"
newMeet.meetStatus = "Active"
let request = NSFetchRequest(entityName: "Gymnast")
request.predicate = NSPredicate(format: "isActive = %#", "Yes")
do {
let results = try AD.managedObjectContext.executeFetchRequest(request) as! [Gymnast]
for result in results {
let newMeets = result.meets?.mutableCopy() as! NSMutableSet
newMeets.addObject(newMeet)
result.meets = newMeets.copy() as? NSSet
print("\(result.fullName!)")
}
} catch {
fatalError("Failed to save data")
}
AD.saveContext()
self.navigationController?.popViewControllerAnimated(true)
You describe the relationship as one Gymnast to many Meets. If it's a one-to-many relationship, that implies that each Meet can only be associated with a single Gymnast, although each Gymnast can be associated with more than one Meet.
If your description of the relationship is correct, then what you're seeing is exactly what would be expected. Since each Meet can only have one Gymnast, each pass through the loop reassigns that relationship from the one set in the previous pass.
I'm guessing that the relationship should be many-to-many, not one-to-many, since each Meet can presumably involve more than one Gymnast.

NSPredicate: More AND condition after ANY is possible?

[NSPredicate predicateWithFormat:#"ANY (spaceVariableDomains.domain.name = %# AND spaceVariableDomains.variable.val = %#)", ...];
I want to write something like above into NSPredicate, but it is unable to parse the format string. Is it possible somehow compound conditions after ANY? Do I need to use SUBQUERY?
Finally I used SUBQUERY approach, basically this expression helped me:
SUBQUERY(spaceVariableDomains, $x, $x.domain.name = 'xxx' and $x.variable.val = 'yyy').#count > 0 AND SUBQUERY(spaceVariableDomains, $x, $x.domain.name = 'zzz' and $x.variable.val = 'www').#count > 0

Xcode SQLite - is there a simpler way?

This is a general question and not the usual StackOverflow "can someone please correct my syntax mistake..."
SQL is the most wonderfully condensed language, so in one line I can save a record. e.g.
INSERT INTO database VALUES (4,'Nilsen', 'Johan', 'Bakken 2', 'Stavanger')
But if I want to save/insert a record in Cocoa with SQLite I'll end up writing closer to 14, e.g.
NSString *insertSQL = [NSString stringWithFormat: #"INSERT INTO CONTACTS (name, address, phone) VALUES (\"%#\", \"%#\", \"%#\")", name.text, address.text, phone.text];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(contactDB,insert_stmt, -1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE) {
self.status.text = #"Contact added";
self.name.text = #"";
self.address.text = #"";
self.phone.text = #"";
} else {
self.status.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(contactDB);
 }
}
I understand what each line is doing but as a Cocoa/SQLite newbie all I want to double check is if there is a simpler way or if I just have to regurgitate this level of verbosity every time I want to do the tritest SQL command...
Many thanks
Use FMDB (an excellent SQLite wrapper).
As an alternative, you could create a function that accepts both the SQL query and the database name. One great use of functions is to simplify the process of writing code.

NSPredicate with Multiple parameters

Hello Need help with predicate. The problem is that I want to have one function which will fetch from database depends on the multiple values it receives, but the values don't always exist, does it mean I have to create several different predicates?
Code below
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"brand_id LIKE %# AND brand_id LIKE %# AND brand_id LIKE %# AND brand_id LIKE %# AND brand_id LIKE %# AND item_category LIKE %# AND item_make LIKE %# AND item_gender LIKE %# || item_price>%# || item_price<%#",
brand_one, brand_two, brand_three, brand_four, brand_five, categoryString, makeString, genderString,[NSNumber numberWithInt:price_bottom],[NSNumber numberWithInt:price_top]];
brand_one, brand_two and etc sometimes exist and sometimes don't.
And how should it for item_gender for example. Let's if there is no gender specified than to have both of them.
Sorry if my description of the problem confusing.
Base on Gobras comments the following code was produces
NSArray *brands = [NSArray arrayWithObjects:brand_one, brand_two, brand_three, brand_four, brand_five, nil];
NSPredicate *predicate_brands = [NSPredicate predicateWithFormat:#"brand_id like %#" argumentArray:brands];
Logical explanation of what what search function should
fetchrequest
predicate on fetch request
predicate based on 5 brand ids, item id, item category, item make, item gender,
if any of above is empty it should fetch all related entries for example if item category is empty it should fetch all "Jewellery, Watches and etc" but limit it with the rest of the predicate expressions.
Should I create for example compound predicates for brands and than create another one for item category with values "item_category like Jewellery" and "item_category like Watches" and after that how do I exactly bring them together?
Assemble an appropriate collection of individual predicates into NSCompoundPredicate
as in
NSMutableArray *parr = [NSMutableArray array];
if([brand_one length]) {
[parr addObject:[NSPredicate predicateWithFormat:#"brand_id LIKE %#",myBrandId]];
}
if([brand_two length]) {
// etc
}
NSPredicate *compoundpred = [NSCompoundPredicate andPredicateWithSubpredicates:parr];
You can stack up your predicates with orPredicateWithSubpredicates: and andPredicateWithSubpredicates: and NSCompoundPredicate being a NSPredicate itself can be compounded.
See
https://developer.apple.com/documentation/foundation/nscompoundpredicate
For those who are interested in Swift!
let arrPred = [NSPredicate(format: "yourAttribute LIKE %#", toCompareID), NSPredicate(format: "yourAttribute2 LIKE %#", toCompareID2)] //Add as many predicates you want in short make your query
let compoundpred:NSPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: arrPred)
var arrFiltered = yourArray.filteredArrayUsingPredicate(compoundpred)
It's pretty simple to build the predicate dynamically: combine your predicate format string with desired number of conditions and build corresponding NSMutableArray with the arguments. [NSPredicate predicateWithFormat:argumentArray:] will do the rest.

NSPredicateEditor and relationships

I've seen that every predicate that works in a query with a relationship contains at the start the words ANY or ALL (ie: ANY tags.name LIKE[c] "car"), the fact is, if I remove it (ie: tags.name LIKE[c] "car"), the result is wrong or I get a message like this one : Can't do regex matching on object.
Since i'm using an NSPredicateEditor their is no ANY or ALL that starts my query, so it always fail.
The Predicates returned is always like the second exemple (no ANY or ALL).
Do I have to subclass the NSPredicateRowTemplateEditor, in order to add myself the ANY or ALL in my predicate, or is their another way?
Same thing with the dates... my dates are saved in this format: YYYY-MM-DD HH:mm:ss, but the NSPredicateEditor use DD/MM/YYYY, so each time I try a date comparaison, it does not work. Do I also have to subclass the RowEditor, in order to change the date format?
Thank you.
Here you go:
class RowTemplateRelationshipAny: NSPredicateEditorRowTemplate {
override func predicate(withSubpredicates subpredicates: [NSPredicate]?) -> NSPredicate{
let predicate: NSComparisonPredicate = super.predicate(withSubpredicates: subpredicates) as! NSComparisonPredicate
let newPredicate = NSComparisonPredicate(leftExpression: predicate.leftExpression, rightExpression: predicate.rightExpression, modifier: .any, type: predicate.predicateOperatorType, options: predicate.options)
return newPredicate
}
}
class RowTemplateRelationshipAll: NSPredicateEditorRowTemplate {
override func predicate(withSubpredicates subpredicates: [NSPredicate]?) -> NSPredicate{
let predicate: NSComparisonPredicate = super.predicate(withSubpredicates: subpredicates) as! NSComparisonPredicate
let newPredicate = NSComparisonPredicate(leftExpression: predicate.leftExpression, rightExpression: predicate.rightExpression, modifier: .all, type: predicate.predicateOperatorType, options: predicate.options)
return newPredicate
}
}
Just change your row template class in IB to RowTemplateRelationshipAny or RowTemplateRelationshipAll.

Resources