Magical Record : sum aggregation - objective-c

I'm doing an aggregation on entities and the code is :
NSPredicate *betweenInterval = [NSPredicate predicateWithFormat:#"(date >= %#) AND (date < %#)", [interval.start value], [interval.end value]];
NSNumber * nbPoints = [OMSPointsEventEntity MR_aggregateOperation:#"sum:" onAttribute:#"nbPoints" withPredicate:betweenInterval];
return [nbPoints intValue];
And NbPoints is nil, I don't know why...
Hint : attribute NbPoints on my entity is a NSNumber should it be an int ?

I don't know about MR, but in standard Cord Data the aggregate functions have to be preceded by #. Maybe you should look this up in the MR documentation.
...MR_aggregateOperation:#"#sum"... // ???
If you can get an array of all the PointsEvent entities, you could calculate it yourself after the fetch:
NSNumber *sum = [fetchedObjects valueForKeyPath:#"#sum.nbPoints"];
As for the attribute data type, your setup seems correct: int32 or similar or float in the Core Data model, NSNumber in your NSManagedObject subclass.

Related

NSArray filter using NSPredicate, comparing dates

I have a Person entity where the attribute date_of_birth is declared as NSString. If I have an array of 'Person' instances and I need to filter them down to only those whose date_of_birth is less that 25/11/2005 I am using a predicate whose format when NSLogged is:
FUNCTION(date_of_birth, "yyyy_MM_dd_dateFormat") <[cd] CAST(154575908.000000, "NSDate")
where yyyy_MM_dd_dateFormat() is a category method on NSString that returns the string instance as a date.
I am not getting the expected results. Am I doing something wrong, and what is the bit where it says CAST(154575908.000000, "NSDate" and is that valid?
UPDATE: changing the date_of_birth attribute type to NSDate is not an option at the moment due to the size, maturity and complexity of the project.
Dates are best represented by NSDate, which implements inequality via earlierDate: and laterDate: methods. These answer the earlier/later date between the receiver and the parameter.
Your conversion method probably looks something like this ...
// return an NSDate for a string given in dd/MM/yyyy
- (NSDate *)dateFromString:(NSString *)string {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd/MM/yyyy"];
return [formatter dateFromString:string];
}
The array can be filtered with a block-based NSPredicate that uses NSDate comparison...
NSDate *november25 = [self dateFromString:#"25/11/2005"];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(Person *person, NSDictionary *bind){
// this is the important part, lets get things in NSDate form so we can use them.
// of course it would be quicker to alter the data type, but we can covert on the fly
NSDate *dob = [self dateFromString:person.date_of_birth];
return date_of_birth == [november25 earlierDate:dob];
}];
// assumes allPeople is an NSArray of Person objects to be filtered
// and assumes Person has an NSString date_of_birth property
NSArray *oldPeople = [allPeople filteredArrayUsingPredicate:predicate];
Here, person date is type of NSDate.
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"date >= %#",person.date];
list=[list filteredArrayUsingPredicate:predicate2];
But in case you saved with NSString type , than you need to cast comparison date into string than use like instead of >=

NSPredicate to many like a <----->> b----->c

I have entities: Language, Proper and Answer.
model look like Language{A:name(NSString), R:propers(NSSet)} --->> Proper{A:name(NSString), R:answer(Answer)} ---> Answer{A:answer(NSString)}
So, i got NSDictionary with params: {#"key1", #"value1"}, {#"key2", #"value2"}... i
I need create NSPredicate from this dictionary to get all Languages where propers.name = key[i] and propers.answer.answer = value[i] from my NSDictionary.
Example:
C++
level : high
try/catch : yes
typization : static
Java
level : high
try/catch : yes
typization : dynamic
NSDictionary : {level : hight}, {try/catch : yes}, {typization : dynamic}
//make and set NSPredicate to array controller
//array controller arrangedObjects will return Java
Sorry for bad grammar :/
Update
*After 2 weeks of sleepless nights and work on expert system teacher took a laboratory without checking it. Kill me please. Thanks a lot to all of you.*
I am just guessing what you want to do so here is a code:
- (NSPredicate *)constructPredicateWithDictionary:(NSDictionary *)dictionary
{
NSArray *allKeys = [dictionary allKeys];
NSMutableArray *predicates = [NSMutableArray array];
for (NSString *key in allKeys) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SUBQUERY(B, $B, $B.key = %# && $B.C.value = %#).#count > 0", key, [dictionary valueForKey:key]];
[predicates addObject:predicate];
}
//not quite sure what you need so I am guessing
NSPredicate *finalAndPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:predicates]; //if you want all the predicates to be concatenated with and '&&' - logical expression - so all of the subqueries have to be correct
NSPredicate *finalOrPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates]; //if you want all the predicates to be concatenated with or '||' - logical expression - so any of the subqueries has to be correct
return finalOrPredicate; //return the needed predicate
}
You need from SUBQUERY. Something like:
[NSPredicate predicateWithFormat:#"B.key = %# AND SUBQUERY(B, $B, $B.C.value = %#).#count > 0", key, value];
The SUBQUERY iterates through the objects and you can also have nested SUBQUERYs if you have more than one to-many relationships.
You can use and "ANY ..." but it doesn't work in all cases.

KVC - Filter Sets

I have a simple data model (Core Data), but here is a stripped down version of it:
Account
----------------------------------------------------
NSSet<Transaction> transactions
Transaction
----------------------------------------------------
Account account
NSNumber amount
NSNumber type ( type ∈ ( 0->'Credit', 1->'Debit' ) )
I want to get an account's total balance, however, I don't want to mark a debit as a negative number. I just want it to be an amount with a type of debit.
Currently, before I added the debits in, I could get the balance via this function in Account:
- (NSNumber *)currentBalance
{
return [self.transactions valueForKeyPath:#"#sum.amount"];
}
Obviously, that will only work if I make debits negative. Is there an elegant solution here, to perhaps filter the set into credits and debits, sum those, and perform a difference? Will that maintain accuracy? I've looked into NSPredicate, but I'm not sure how exactly to proceed.
Here is what my research has led me to:
- (NSNumber *)currentBalance
{
// credits
NSPredicate *creditPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [((Transaction *)evaluatedObject).type intValue] == kTransactionTypeCredit;
}];
NSSet *credits = [self.transactions filteredSetUsingPredicate:creditPredicate];
NSDecimalNumber *totalCredits = [credits valueForKeyPath:#"#sum.amount"];
// debits
NSPredicate *debitPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [((Transaction *)evaluatedObject).type intValue] == kTransactionTypeDebit;
}];
NSSet *debits = [self.transactions filteredSetUsingPredicate:debitPredicate];
NSDecimalNumber *totalDebits = [debits valueForKeyPath:#"#sum.amount"];
// do the math
return [totalCredits decimalNumberBySubtracting:totalDebits];
}
Whilst it works, it seems like an excessive amount of work. Is it the best way?

Filtering NSMutableArray based on enum property

I've got an NSMutableArray filled with objects of type "GameObject". GameObject has a number of properties, one of which being "gameObjectType" . "gameObjectType" is of type GameObjectTypeEnum. I want to be able to filter this NSMutableArray so only GameObjects of a certain type are returned. I've got the following in place, but it's giving me a "BAD ACCESS" error:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"gameObjectType = %#", gameObjectType];
return [gameObjects filteredArrayUsingPredicate:predicate];
Is it possible to pass a "custom" type (ie, this enum I've defined) into the predicateWithFormat call?
The string format specifier %# indicates an object, while you're passing an integral value. You probably want to typecast the gameObjectType to an int and use the %d specifier. Take a look at the string format specifiers for more info.
- (NSArray *)arrayFilteredByType:(enumType)type {
//type is an NSUInteger property of the objects in the array
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type = %d", type];
return [self.array filteredArrayUsingPredicate:predicate];
}

filtering NSArray into a new NSArray in Objective-C

I have an NSArray and I'd like to create a new NSArray with objects from the original array that meet certain criteria. The criteria is decided by a function that returns a BOOL.
I can create an NSMutableArray, iterate through the source array and copy over the objects that the filter function accepts and then create an immutable version of it.
Is there a better way?
NSArray and NSMutableArray provide methods to filter array contents. NSArray provides filteredArrayUsingPredicate: which returns a new array containing objects in the receiver that match the specified predicate. NSMutableArray adds filterUsingPredicate: which evaluates the receiver’s content against the specified predicate and leaves only objects that match. These methods are illustrated in the following example.
NSMutableArray *array =
[NSMutableArray arrayWithObjects:#"Bill", #"Ben", #"Chris", #"Melissa", nil];
NSPredicate *bPredicate =
[NSPredicate predicateWithFormat:#"SELF beginswith[c] 'b'"];
NSArray *beginWithB =
[array filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { #"Bill", #"Ben" }.
NSPredicate *sPredicate =
[NSPredicate predicateWithFormat:#"SELF contains[c] 's'"];
[array filteredArrayUsingPredicate:sPredicate];
// array now contains { #"Chris", #"Melissa" }
There are loads of ways to do this, but by far the neatest is surely using [NSPredicate predicateWithBlock:]:
NSArray *filteredArray = [array filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
return [object shouldIKeepYou]; // Return YES for each object you want in filteredArray.
}]];
I think that's about as concise as it gets.
Swift:
For those working with NSArrays in Swift, you may prefer this even more concise version:
let filteredArray = array.filter { $0.shouldIKeepYou() }
filter is just a method on Array (NSArray is implicitly bridged to Swift’s Array). It takes one argument: a closure that takes one object in the array and returns a Bool. In your closure, just return true for any objects you want in the filtered array.
Based on an answer by Clay Bridges, here is an example of filtering using blocks (change yourArray to your array variable name and testFunc to the name of your testing function):
yourArray = [yourArray objectsAtIndexes:[yourArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [self testFunc:obj];
}]];
If you are OS X 10.6/iOS 4.0 or later, you're probably better off with blocks than NSPredicate. See -[NSArray indexesOfObjectsPassingTest:] or write your own category to add a handy -select: or -filter: method (example).
Want somebody else to write that category, test it, etc.? Check out BlocksKit (array docs). And there are many more examples to be found by, say, searching for e.g. "nsarray block category select".
Assuming that your objects are all of a similar type you could add a method as a category of their base class that calls the function you're using for your criteria. Then create an NSPredicate object that refers to that method.
In some category define your method that uses your function
#implementation BaseClass (SomeCategory)
- (BOOL)myMethod {
return someComparisonFunction(self, whatever);
}
#end
Then wherever you'll be filtering:
- (NSArray *)myFilteredObjects {
NSPredicate *pred = [NSPredicate predicateWithFormat:#"myMethod = TRUE"];
return [myArray filteredArrayUsingPredicate:pred];
}
Of course, if your function only compares against properties reachable from within your class it may just be easier to convert the function's conditions to a predicate string.
NSPredicate is nextstep's way of constructing condition to filter a collection (NSArray, NSSet, NSDictionary).
For example consider two arrays arr and filteredarr:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",#"c"];
filteredarr = [NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
the filteredarr will surely have the items that contains the character c alone.
to make it easy to remember those who little sql background it is
*--select * from tbl where column1 like '%a%'--*
1)select * from tbl --> collection
2)column1 like '%a%' --> NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",#"c"];
3)select * from tbl where column1 like '%a%' -->
[NSMutableArray arrayWithArray:[arr filteredArrayUsingPredicate:predicate]];
I hope this helps
Checkout this library
https://github.com/BadChoice/Collection
It comes with lots of easy array functions to never write a loop again
So you can just do:
NSArray* youngHeroes = [self.heroes filter:^BOOL(Hero *object) {
return object.age.intValue < 20;
}];
or
NSArray* oldHeroes = [self.heroes reject:^BOOL(Hero *object) {
return object.age.intValue < 20;
}];
The Best and easy Way is to create this method And Pass Array And Value:
- (NSArray *) filter:(NSArray *)array where:(NSString *)key is:(id)value{
NSMutableArray *temArr=[[NSMutableArray alloc] init];
for(NSDictionary *dic in self)
if([dic[key] isEqual:value])
[temArr addObject:dic];
return temArr;
}
Another category method you could use:
- (NSArray *) filteredArrayUsingBlock:(BOOL (^)(id obj))block {
NSIndexSet *const filteredIndexes = [self indexesOfObjectsPassingTest:^BOOL (id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
return block(obj);
}];
return [self objectsAtIndexes:filteredIndexes];
}