NSPredicate confusion with Nested Arrays of custom objects - objective-c

I have a custom object, which is in the format:
#interface GroupModel : NSObject
#property (strong, nonatomic) NSString *groupId;
#property (strong, nonatomic) NSArray *children;
#property (strong, nonatomic) NSArray *instruments;
#property (strong, nonatomic) NSString *name;
#end
The instruments array is an array of custom models in the form of:
#interface InstrumentModel : NSObject
#property (strong, nonatomic) NSString *instrumentId;
#property (strong, nonatomic) NSString *name;
#end
And the children array in the GroupModel, is an array of GroupModel objects.
The reason behind this is to create a tree-like structure, so a root group can have multiple child groups, each of them can contain instruments and/or child groups.
Kinda like a folder/file format
Now what I'm trying is to search for a given instrument Id in a given GroupModel.
The instrumentId might be in the given model in the instruments array, or it might be in any children group models instruments array.
What kind of predicate would I need to be using, as I'm confused.
Would this require a subquery ?

Ok, This has worked for me:
//Create an array to add all InstrumentGroup Objects
NSMutableArray * allChildren = [NSMutableArray new];
//add the root group instruments
[allChildren addObjectsFromArray:rootGroup.instruments];
//now add the children's of children Instruments using the unionOfArrays Collection operator
[allChildren addObjectsFromArray:[rootGroup.children valueForKeyPath:#"#unionOfArrays.instruments"]];
//add the children's children instruments
[allChildren addObjectsFromArray:[rootGroup.children valueForKeyPath:#"#unionOfArrays.children.#unionOfArrays.instruments"]];
//filter the Instrument objects
predicate = [NSPredicate predicateWithFormat:#"ANY instruments.instrumentId == %#",selectedInstrumentId];
filtered = [allChildren filteredArrayUsingPredicate:predicate];
if (filtered.count>0) {
//found the instrument!
InstrumentModel *instrument = filtered.firstObject;
}

Related

Searching and Modifying NSMutableArray object with updated values

I have constructed my model object with following.
#property (strong, nonatomic) NSString *id;
#property (strong, nonatomic) NSString *somevalue;
#property (strong, nonatomic) NSString *another value;
#property (strong, nonatomic) NSString *xtz;
#property (strong, nonatomic) NSString *st;
#property (strong, nonatomic) NSMutableArray *sc;
Now my mutable array is filled with following sample objects,
{a = 1;b = 6;c = 0;},{a = 2;b = 7;c = 0;},{a = 2;b = 8;c = 0;},{a = 3;b=9;c = 0;}
There are roughly 200 of such objects in my array. Now I cannot figure out a decent way of looping through the array updating the value of for example 'c' to '1' where 'a' ==1.
I could use a for loop like that:
for(int i = 0 ; i <myobject.sc.count; i++) {
NSLog(#"%#",[[myobject sc]objectAtIndex:i]);
}
It would allow me to iterate through the array. But still face the problem of looping through values contains within each object. Maybe a nested loop?
All you need is a simple loop:
for (WhateverDataType *data in myobject.sc) {
if (data.a == 1) {
data.c = 1
// break // uncomment if you only want to update the first match
}
}
This gives you a general idea for the solution. The specific syntax depends on what type of objects are actually in the mutable array.

How to do deep copying Objective-C

There is a class Patient with properties:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) Symptoms *symptoms;
#property (assign, nonatomic) Status status;
#property (weak, nonatomic) id <PatientDelegate> delegate;
There is a class Symptoms with properties:
#property (assign, nonatomic) CGFloat temperature;
#property (assign, nonatomic) BOOL headache;
#property (assign, nonatomic) BOOL stomach_ache;
Both classes implement protocol NSCopying:
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Patient *newPatient = [[[self class] allocWithZone:zone] init];
[newPatient setName:self.name];
[newPatient setSymptoms:self.symptoms];
[newPatient setStatus:self.status];
[newPatient setDelegate:self.delegate];
return newPatient;
}
- (nonnull id)copyWithZone:(nullable NSZone *)zone {
Symptoms *newSymptoms = [[[self class] allocWithZone:zone] init];
[newSymptoms setTemperature:self.temperature];
[newSymptoms setHeadache:self.headache];
[newSymptoms setStomach_ache:self.stomach_ache];
return newSymptoms;
}
Also there is a class Doctor:
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSMutableArray *history;
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history;
- (void)report;
When patient gets well, doctor calls method makeNoteIn:
- (void)makeNoteIn:(Patient *)patient card:(NSMutableArray *)history {
Patient *newRecord = [patient copy];
[history addObject:newRecord];
}
After the record is made, all property of the patient return to the original values. While we are in the method makeNoteIn and the current patient is proceeded, in history there is link to this object which has correct property values. As soon as we exit the method or start to proceed another patient, all the property values reset to the initial value.
I tried to realise copying, but something still wrong.
When you want to deep-copy an object, you have to implement copy on all the substructures:
[newPatient setName:[self.name copy]];
[newPatient setSymptoms:[self.symptoms copy]];
Otherwise they will still reference the same object and changing one will affect all.
Note that you can do that automatically by declaring the properties as copy:
#property (copy, nonatomic) NSString *name;
#property (copy, nonatomic) Symptoms *symptoms;
It's common to use copy with NSString and NSArray to prevent assigning NSMutableString and NSMutableArray which could be changed externally by mistake. Make sure you implement NSCopying on Symptoms.

NSPredicate to filter the Contact Number in NSMutableArray

// Person.h
#interface Person : NSObject
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSMutableArray *phoneNumbers;
#property (nonatomic, strong) UIImage *personImage;
#property (nonatomic, strong) NSNumber* recordID;
- (id)initWithPerson:(Person *)person;
#end
I am fetching the contact from the address book and adding it into custom class Person.
Now for every contact in address book there might be multiple number so i have used NSMutableArray phoneNumbers , Now i need to search the contact and get the name of the Person .
I have Tried doing like this :
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"phoneNumbers like %#",contactNumber];
NSArray *filtered = [addressBookData filteredArrayUsingPredicate:predicate];
I am getting Nil in filtered array. Please suggest me how to solve this problem.
Instead of like you should be using contains to compare the contents of the array rather than the array itself.

NSDictionary: Comparing NSDictionaries

There are two of files. Lets call them fileOne and fileTwo
Each has several NSMutableDictionary properties with identical names. To list a few:
#property (retain, nonatomic) NSMutableDictionary * lunchStartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * lunchLocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * lunchLocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * activity1NameObject;
#property (retain, nonatomic) NSMutableDictionary * activity1StartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * activity1LocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * activity1CommentsFieldObject;
#property (retain, nonatomic) NSMutableDictionary * activity1LocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * activity2NameObject;
#property (retain, nonatomic) NSMutableDictionary * activity2StartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * activity2LocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * activity2CommentsFieldObject;
#property (retain, nonatomic) NSMutableDictionary * activity2LocationAddressObject;
I would like to compare the dictionaries with the same name in the two files by calling the method below (or something similar):
-(NSMutableDictionary *)cellColorForChanges:(NSMutableDictionary *)newdictionary :(NSMutableDictionary *)oldDictionary;
{
if(![newdictionary isEqualToDictionary:oldDictionary])
{
[newdictionary setValue:#"UIColor yellowColor" forKey:#"cellColor"];
}
return newdictionary;
}
I'm trying avoid writing code for each NSMutableDictionary manually. Is there a way to avoid the following:
if(![fileOne.lunchStartTimeObject isEqualToDictionary:fileTwo.lunchStartTimeObject])
{
fileOne.lunchStartTimeObject setValue:#"UIColor yellowColor" forKey:#"cellColor"];
}
I'm having trouble figuring out the most efficient way to accomplish the above. Is it somehow possible to send each dictionary to a method and get back the dictionary (updated with another key, if it's not equal)? Or what I'm trying to avoid is unavoidable?
You are running into this problem because you are not abstracting your objects sufficiently. It seems to me that your property list above is ridiculously redundant. Even the simple code code you provided is hardly readable.
Try to think of your problem in a more conceptional way. Try to think of objects that could encapsulate the functionality you are looking for.
Looking at your properties you probably want a class like this
#interface Activity : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSDate *startTime;
#property (nonatomic, strong) Location *location;
-(BOOL)isEqualToActivity:(Activity*)activity;
#end
Maybe you need a location class that stores more information about a location; perhaps you could use even more fields like firstName, lastName; maybe you need a type (enum or string) property that tells what kind of activity it is, etc. -- you get the idea.
As indicated, you could write your own comparison method where you could tweak for allowing more or less strict capital or small letters, number formats etc.

Objective C - NSFetchRequest with category unique

I'm using Core Data for my iPhone app.
Initially I have a UITableViewController that just lists all the "Stores".
However, now we realized that list is getting too long, and would like to break it down into 2 layers of UITableViewController. The first one being "States" and the 2nd being the stores in the selected State.
I figured out how to grab all stores in a certain State
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"state.abbreviation == 'AA'"]];
How can I using NSFetchRequest, just to grab the list of States? Something like this sql statement maybe? Is it possible?
SELECT DISTINCT abbreviation FROM Stores;
Below is my Model (simplified)
Store.h
#class State;
#interface Store : ManagedAppObject {
#private
}
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) State * state;
#end
State.h
#interface State : ManagedAppObject {
#private
}
#property (nonatomic, retain) NSString * abbreviation;
#property (nonatomic, retain) NSString * name;
#end
Thank you,
Tee
To get unique results you'd usually use
fetchRequest.propertiesToFetch = [NSArray arrayWithObject:#"<#propertyName#>"];
fetchRequest.returnsDistinctResults = YES;
fetchRequest.resultType = NSDictionaryResultType;
But unless I'm missing something here, that's probably not what you should do. Why don't you just set your fetchRequest to fetch State entities in the first place? Then use the state.stores relation (which hopefully you have modeled as the inverse of store.state) to get the data for your second 'layer' tableView.