Parse JSON to a tree structure - ios

I'm working on a project that requires a tableView list of categorized grocery items. Each category can have n depth. The JSON response from the API looks like this.
"items":[
{
"id":"5366f8d3e4b0e44dc2d4a6fb",
"name":"String Cheese"
"description":"Sargento String Cheese",
"categorization":[
[
"Dairy",
"Cheese"
]
]
},
{
"id":"5366f8d3e4b0e44dc2d1a6fb",
"name":"Budlight 6-pk"
"description":"Budlight 12-pk",
"categorization":[
[
"Beverages",
"Alcohol",
"Beer"
]
]
}
]
Right now I'm creating Item objects from the item dictionaries and storing them in a mutable array like below.
NSArray *itemsArray = [response objectForKey:items];
NSMutableArray *itemsMutableArray = [[NSMutableArray alloc] init];
for(NSDictionary *itemDict in itemsArray){
Item *itemObj = [[Item alloc] initWithDictionary:itemDict]
[itemsMutableArray addObject:itemObj];
}
I would like to loop through itemsMutableArray and create a tree data structure that has a path from the root to each of the items. Then, I would like to be able to use the tree as a datasource for tableViews in each level of category.
Here's what my Item class header looks like.
#interface Item : NSObject
#property (nonatomic, strong) NSString *id;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *description;
#property (nonatomic, strong) NSArray *categorization;
#end
...and the implementation
#import "Item.h"
#implementation Item
- (id)initWithDictionary:(NSDictionary *)objDictionary{
if (self = [super init]) {
self.id = [objDictionary valueForKey:#"id"];
self.name = [objDictionary valueForKey:#"name"];
self.description = [objDictionary valueForKey:#"description"];
self.categorization = [objDictionary valueForKey:#"categorization"];
}
return self;
}
#end
I am not very familiar with tree data structures and recursion. I would greatly appreciate any help on how to approach this. Thanks!

If you need simple node tree data structure. How about this way?
Hope this little help.
Header
#interface ItemCategory : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic) ItemCategory *parent;
#property (nonatomic, strong) NSMutableArray *children;
-(id)initWithName:(NSString *)n parent:(ItemCategory *)p;
#end
#interface CategoryTree : NSObject
#property (nonatomic, strong) ItemCategory *root;
-(ItemCategory *)_getChildCategory:(ItemCategory *)category name:(NSString *)name;
-(ItemCategory *)_addChildCategory:(ItemCategory *)category name:(NSString *)name;
-(void)_dumpCategory:(ItemCategory *)category depth:(int)depth;
-(void)dump;
-(ItemCategory *)getCategory:(NSArray *)arr;
-(void)addCategory:(NSArray *)arr;
#end
Source
#implementation CategoryTree
#synthesize root;
-(id)init {
if (self = [super init]) {
root = [[ItemCategory alloc] initWithName:#"root" parent:nil];
}
return self;
}
-(ItemCategory *)_getChildCategory:(ItemCategory *)category name:(NSString *)name {
for (ItemCategory *child in category.children)
if ([child.name isEqualToString:name])
return child;
return nil;
}
-(ItemCategory *)_addChildCategory:(ItemCategory *)category name:(NSString *)name {
ItemCategory *child = [self _getChildCategory:category name:name];
if (child)
return child;
child = [[ItemCategory alloc] initWithName:name parent:category];
[category.children addObject:child];
return child;
}
-(void)_dumpCategory:(ItemCategory *)category depth:(int)depth{
NSString *parentStr = #"";
ItemCategory *parent = category.parent;
while (parent) {
parentStr = [NSString stringWithFormat:#"%#%#%#", parent.name, parentStr.length > 0 ? #">" : #"", parentStr];
parent = parent.parent;
}
NSLog(#"%#%#%#", parentStr, parentStr.length > 0 ? #">" : #"", category.name);
for (ItemCategory *child in category.children) {
[self _dumpCategory:child depth:depth + 1];
}
}
-(void)dump {
[self _dumpCategory:root depth:0];
}
-(ItemCategory *)getCategory:(NSArray *)arr {
ItemCategory *category = root;
for (NSString *categoryName in arr) {
category = [self _getChildCategory:category name:categoryName];
if (!category)
return nil;
}
return category;
}
-(void)addCategory:(NSArray *)arr {
if ([self getCategory:arr])
return;
ItemCategory *category = root;
for (NSString *categoryName in arr) {
ItemCategory *childCategory = [self _getChildCategory:category name:categoryName];
if (!childCategory) {
childCategory = [self _addChildCategory:category name:categoryName];
}
category = childCategory;
}
}
#end
Usage
CategoryTree *tree = [[CategoryTree alloc] init];
[tree addCategory:#[#"Dairy", #"Cheese"]];
[tree addCategory:#[#"Dairy", #"Milk"]];
[tree addCategory:#[#"Beverages", #"Alcohol", #"Beer"]];
[tree addCategory:#[#"Beverages", #"Alcohol", #"Wine"]];
[tree addCategory:#[#"Beverages", #"Non-Alcohol", #"Cola"]];
[tree dump];
Result
root
root>Dairy
root>Dairy>Cheese
root>Dairy>Milk
root>Beverages
root>Beverages>Alcohol
root>Beverages>Alcohol>Beer
root>Beverages>Alcohol>Wine
root>Beverages>Non-Alcohol
root>Beverages>Non-Alcohol>Cola

well I have found a way to implement what you need. I do not know how optimised it is since i do not how many items you'll be receiving . The implementation is given below.
You need to start with adding this dictionary in Item.h #property (nonatomic, strong) NSMutableDictionary *catTree;
Next do this to get the tree
[itemsMutableArray enumerateObjectsUsingBlock:^(Item *itm, NSUInteger i,BOOL *stop){
itm.catTree = [NSMutableDictionary dictionary];
NSString *dairy = #"",*beverage = #"";
for (NSArray *catArray in itm.categorization) {
/*
Everything below is written assuming the format of the JSON will be "as-is"
*/
if ([catArray containsObject:#"Dairy"]) {
//Take everything except Dairy
NSArray *stripedArray = [catArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != \"Dairy\""]];
int i = 0;
//Loop through the array to get any sub categories.
while (i < stripedArray.count) {
dairy = [dairy stringByAppendingString:[NSString stringWithFormat:(i == stripedArray.count-1)?#"%# ":#"%#->",stripedArray[i]]]; //Space at the end to account for similar entry in the same category for e.g two dairy products.
i++;
}
} else if ([catArray containsObject:#"Beverages"]) {
NSArray *stripedArray = [catArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != \"Beverages\""]];
int i = 0;
while (i < stripedArray.count) {
beverage = [beverage stringByAppendingString:[NSString stringWithFormat:(i == stripedArray.count-1)?#"%# ":#"%#->",stripedArray[i]]];
i++;
}
}
}
//Set the category tree for every item using a dictionary
[itm.catTree setValue:dairy forKey:#"Dairy"];
[itm.catTree setValue:beverage forKey:#"Beverage"];
NSLog(#"%#",itm.catTree);
}];
the above code gives the following output for your json
{
Beverage = "";
Dairy = "Cheese ";
}
{
Beverage = "Alcohol->Beer ";
Dairy = "";
}
For multiple beverages
{
Beverage = "Alcohol->Beer Alcohol->Wine->Red Soda->Coke ";
Dairy = "";
}
Hope this helps.

Related

Custom object returns NSDictionary as type

my object CCategory.h
#interface CCategory : NSObject
#property(strong, nonatomic) NSNumber * _Nonnull categoryId;
#property(strong, nonatomic) NSNumber * _Nonnull originalId;
#property(strong, nonatomic) NSString * _Nonnull name;
#property(strong, nonatomic) NSString * _Nonnull type;
#property(nonatomic, strong) CCategory * _Nullable parent;
#property (nullable, nonatomic, retain) NSOrderedSet<CCategory *> *children;
- (instancetype _Nonnull )initWithId:(NSNumber *_Nullable)categoryId
andOriginalId:(NSNumber *_Nullable)originalId
andName:(NSString *_Nonnull)name
andType:(NSString *_Nonnull)type
andParent:(CCategory *_Nullable)parent
andChildren:(NSOrderedSet<CCategory *> *_Nullable)children NS_DESIGNATED_INITIALIZER;
#end
CCategory.m
#implementation CCategory
- (instancetype)init {
return [self initWithId:0 andOriginalId:0 andName:#"" andType:#"" andParent:nil andChildren:nil];
}
- (instancetype)initWithId:(NSNumber *)categoryId
andOriginalId:(NSNumber *)originalId
andName:(NSString *)name
andType:(NSString *)type
andParent:(CCategory *)parent
andChildren:(NSOrderedSet<CCategory *> *)children {
self = [super init];
if (self) {
self.categoryId = categoryId;
self.originalId = originalId;
self.name = name;
self.type = type;
self.parent = parent;
self.children = children;
}
return self;
}
#end
This is how I check class type:
CCategory * firstItem = [itemsArray objectAtIndex:0];
CCategory *child = [firstItem.children objectAtIndex:0];
NSString *className = NSStringFromClass([child class]);
NSLog(#"First Item is: %#", className);
firstItem returns type CCategory, child returns type NSDictionary
After receiving from database object contains all data, but children for some reason is the NSDictionary type, not CCategory class type. Why is that? and how can I make children type CCategory?
Because you declare some object of some class doesn't mean that it's of the correct class.
If you write for instance
NSArray *array = [#[#"Hello"] firstObject];
array will be in fact a NSString object.
So, when you parse your response and create your CCategory object from what I guess a NSDictionary object.
That's why children seems to be in fact an NSOrderedSet of NSDictionary and not of CCategory objects.
A possible way to do it, is to call recursively initWithId:andOriginalId:andName:andType:andParent:andChildren: for the children.
So instead of self.children = children;
NSMutableOrderedSet *childrenSet = [[NSMutableOrderedSet alloc] init];
for (NSDictionary *aChildDict in [children array])
{
CCategory *aChild = [CCategory alloc] initWithId:aChildDict[keyWhereThereIsID], etc.]
[childrenSet addObject:aChild];
}
self.children = childrenSet;
But that's more of a hack to set it like that in the init method, because it says children should be NSOrderedSet<CCategory *> *.
So it's up to you, to either rename the method to be clear of what it does and maybe accept a NSOrderedSet<NSDictionary *> * for children instead, parse it before, create another one, etc.
One possible lazy option is to do that:
Rename to andChildren:(NSOrderedSet *)children
NSMutableOrderedSet *childrenSet = [[NSMutableOrderedSet alloc] init];
for (id *aChildObject in [children array])
{
CCategory *aChild = nil;
if ([aChildObject isKindOfClass:[NSDictionary class]]) //Need parsing
{
NSDictionary *aChildDict = (NSDictionary *)aChildObject;
aChild = [CCategory alloc] initWithId:aChildDict[keyWhereThereIsID], etc.];
}
else if ([aChildObject isKindOfClass:[CCategory class]]) //Already a CCategory Object
{
aChild = (CCategory *)aChildObject;
}
else
{
NSLog(#"Ooops, child of wrong class: %#", NSStringFromClass([aChildObject class]);
}
if (aChild) { [childrenSet addObject:aChild]; }
}
self.children = childrenSet;

How to add object in singleton NSMutableArray

I used to store the array data downloaded from the server.
But I can not save them in the singleton array.
It seems without access to the object.
Why ulatitude, ulongitude, uaccuracy, uplacename is nil?...
in .h file
#import <Foundation/Foundation.h>
#interface LocationData : NSObject
{
NSMutableArray *ulatitude;
NSMutableArray *ulongitude;
NSMutableArray *uaccuracy;
NSMutableArray *uplacename;
}
#property (nonatomic, retain) NSMutableArray *ulatitude;
#property (nonatomic, retain) NSMutableArray *ulongitude;
#property (nonatomic, retain) NSMutableArray *uaccuracy;
#property (nonatomic, retain) NSMutableArray *uplacename;
+ (LocationData*) sharedStateInstance;
#end
in .m file
#import "LocationData.h"
#implementation LocationData
#synthesize uaccuracy;
#synthesize ulatitude;
#synthesize ulongitude;
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
}
}
return sharedStateInstance;
}
#end
use
[manager POST:urlStr parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"%#",responseObject);
// json response array
if ([responseObject isKindOfClass:[NSArray class]]) {
NSArray *responseArray = responseObject;
NSDictionary *responseDict = [[NSDictionary alloc] init];
LocationData* sharedState = [LocationData sharedStateInstance];
for(NSUInteger i=0; i < responseArray.count; i++)
{
responseDict = [responseArray objectAtIndex:i];
double dlat = [[responseDict objectForKey:#"lat"] doubleValue];
double dlng = [[responseDict objectForKey:#"lng"] doubleValue];
[[sharedState ulatitude] addObject:[NSString stringWithFormat:#"%f",dlat]];
[[sharedState ulongitude] addObject:[NSString stringWithFormat:#"%f",dlng]];
[[sharedState uaccuracy] addObject:[responseDict objectForKey:#"rad"]];
[[sharedState uplacename] addObject:[responseDict objectForKey:#"place_name"]];
}
You always need to initialize your arrays. You should do somewhere before you try to add something to them:
arrayName = [[NSMutableArray alloc] init];
otherwise you'll always get error because they have not been initialized.
In your case you should override your LocationData init function like this:
- (instancetype)init {
self = [super init];
if (self) {
self.yourArrayName = [[NSMutableArray alloc] init];
// And so on....
}
return self;
}
You need to initialize your object properly. Basically your member variables ("ivars") are pointing to nothing ("nil").
This initializer added to your .m file code do the job.
-(instancetype)init {
if ((self = [super init])) {
self.accuracy = [NSMutableArray array];
self.latitude = [NSMutableArray array];
self.longitude = [NSMutableArray array];
self.uplacename = [NSMutableArray array];
}
return self;
}
As a singleton pattern, I'd prefer the following:
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedStateInstance = [[LocationData alloc] init];
});
return sharedStateInstance;
}
Although singletons might not be as bad they are often said to be, I don't thing that this is a good usage for them. Your specific problem has nothing to do with that design choice, though.
Try this code. Write getters for your NSMutableArrays.
#import <Foundation/Foundation.h>
#interface LocationData : NSObject
#property (nonatomic, retain) NSMutableArray *ulatitude;
#property (nonatomic, retain) NSMutableArray *ulongitude;
#property (nonatomic, retain) NSMutableArray *uaccuracy;
#property (nonatomic, retain) NSMutableArray *uplacename;
+ (LocationData*) sharedStateInstance;
#end
#import "LocationData.h"
#implementation LocationData
#synthesize uaccuracy = _uaccuracy;
#synthesize ulatitude = _ulatitude;
#synthesize ulongitude = _ulongitude;
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
}
}
return sharedStateInstance;
}
-(NSMutableArray*)uaccuracy
{
if(_uaccuracy == nil)
{
_uaccuracy = [[NSMutableArray alloc]init];
}
return uaccuracy;
}
-(NSMutableArray*)ulongitude
{
if(_ulongitude == nil)
{
_ulongitude = [[NSMutableArray alloc]init];
}
return ulongitude;
}
-(NSMutableArray*)ulatitude
{
if(_ulatitude == nil)
{
_ulatitude = [[NSMutableArray alloc]init];
}
return ulatitude;
}
-(NSMutableArray*)uplacename
{
if(_uplacename == nil)
{
_uplacename = [[NSMutableArray alloc]init];
}
return uplacename;
}
#end
you don't allocate/init any array...
you can create them in your singleton creation method
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
sharedStateInstance.ulatitude = [[NSMutableArray alloc] init];
// (add others...)
}
}
return sharedStateInstance;
}
Replace your LocationData.m file with below code , this will work . As you have to alloc and init the array then only you can add object in array
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
uaccuracy = [[NSMutableArray alloc]init];
ulatitude = [[NSMutableArray alloc]init];
ulongitude = [[NSMutableArray alloc]init];
uplacename = [[NSMutableArray alloc]init];
}
}
return sharedStateInstance;
}

How to search a word from an array of model which is inside a model array in objective c

What is the best and easiest way to search a word from an array of model which is inside another model array. For example, I have an array which contains model class for students. So students has more than one teacher which is in array of teachers model class. And when i search teacher name , i need to get list of students who has that teacher. I know how to search using predicate from a array of model class.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",#"sarah"];
searchArray = [NSMutableArray arrayWithArray:[studentArray filteredArrayUsingPredicate:predicate]];
I tried this but its not working.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.teachers.teacherName contains[c] %#",#"sarah"];
Student Model class will looks like this.
#property (nonatomic, assign) NSString *name;
#property (nonatomic, strong) NSString *address;
#property (strong, nonatomic) NSArray *teachers;
I know how to do this with splitting array and get it using for loops. Can any one help me to find me an easiest way. Thank you.
okay lets say, your model class has below properties as you mentioned,
for example in Student.h file
#import <Foundation/Foundation.h>
#interface Student : NSObject
#property (nonatomic, strong) NSString *name; //i am changing it to strong
#property (nonatomic, strong) NSString *address;
#property (strong, nonatomic) NSArray *teachers;
- (id)initWithName:(NSString *)name trachers:(NSArray *)teachers address:(NSString *)address;
#end
and in Student.m file
#import "Student.h"
#implementation Student
//custom initialiser
- (id)initWithName:(NSString *)name trachers:(NSArray *)teachers address:(NSString *)address
{
if(self = [super init])
{
self.name = name;
self.teachers = teachers;
self.address = address;
}
return self;
}
#end
and in ViewController.m file i am creating the instance of the Student and adding it to array for example,
in ViewController.h file,
#import <UIKit/UIKit.h>
#import "Student.h"
#property (nonatomic, strong) NSMutableArray *students; //to hold student instances
#property (weak, nonatomic) IBOutlet UISearchBar *searchBar;//to search the teachers
#property (weak, nonatomic) IBOutlet UITextView *textView;//to display the output
#end
and in ViewController.m file,
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_students = [[NSMutableArray alloc] init];
for(int k = 0 ; k < 5 ; k++)
{
NSMutableArray *teachers = [self getTeachersForStudent:k];//a helper method to fill teacher's name
Student *stud = [[Student alloc] initWithName:[NSString stringWithFormat:#"student_%d",k] trachers:teachers address:[NSString stringWithFormat:#"address_%d",k]]; //hear student name will be like "student_0"
[_students addObject:stud]; //finally add it to students array
}
}
//this is the helper method, for testing u can add different same name if you want
- (NSMutableArray *)getTeachersForStudent:(int)k
{
NSMutableArray *array = [[NSMutableArray alloc] init];
switch (k)
{
case 0:
[array addObjectsFromArray:#[#"anoop",#"shan",#"sara",#"peter",#"david",#"gopi"]];
break;
case 1:
[array addObjectsFromArray:#[#"hema",#"isah",#"sara",#"john"]];
break;
case 2:
[array addObjectsFromArray:#[#"venu",#"giri",#"kim",#"madu",#"shan"]];
break;
case 3:
[array addObjectsFromArray:#[#"venu",#"tom",#"yesh",#"vijay",#"shan"]];
break;
case 4:
[array addObjectsFromArray:#[#"vim",#"janu",#"jai",#"kiran",#"shan"]];
break;
default:
break;
}
return array;
}
finally the sorting function in the ViewController.m file add below method, it will take the initial string from search bar and searches the all teachers name and finally gives the student name that will be displayed in the textview
- (void)refreshStudentList
{
__block NSMutableArray *result = [[NSMutableArray alloc] init];
[_students enumerateObjectsUsingBlock:^(Student* _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
__block Student *presentStudent = obj;
__block NSArray *array = [presentStudent valueForKey:#"teachers"];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if([obj rangeOfString:self.searchBar.text options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[result addObject:presentStudent];
}
}];
}];
//at this point result array contains all the student student name of the teacher that u are searched
NSString *resultString = #"";
for(Student *s in result)
{
resultString = [NSString stringWithFormat:#"%# \n %#", resultString, s.name];
}
_textView.text = resultString;
}
add the searhbar delegate method in same ViewController.m and call refreshStudentList method
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[self refreshStudentList];
if (searchText.length == 0)
_textView.text = #""; //simply resetting the text view
}
finally the output will be like below
Try below snippet. It will work
NSArray *allIds = YOURARRAY
__block NSMutableArray *newArray = [[NSMutableArray alloc] init];
[allIds enumerateObjectsUsingBlock:^(NSArray* obj, NSUInteger idx, BOOL *stop) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",#"sarah"];
searchArray = [NSMutableArray arrayWithArray:[studentArray filteredArrayUsingPredicate:predicate]];
[newArray addObject:searchArray];
}];
NSLog(#"%d", [newArray count]);
You can use KVC operators in your NSPredicate, doing something like:
predicate =
[NSPredicate predicateWithFormat:#"teachers.lastName CONTAINS %#", #"Petter" ];
filtered = [studentsArray filteredArrayUsingPredicate:predicate];
#mattt wrote a good example in their NSHipster article (http://nshipster.com/kvc-collection-operators/) about KVC operators in collections, which maybe will be useful to you.

Change or Update NSMuttableDictionary

How to Change or Update NSMuttableDictionary i apply code below
in User.h file the code
#interface User : NSObject
#property (nonatomic, strong) NSNumber *userID;
#property (nonatomic, strong) NSString *avatar;
#property (nonatomic, strong) NSString *firstname;
-(User *)initWithDictionary:(NSDictionary *)dictionary;
#end
then in User.m file the code
#import "User.h"
#implementation User
#synthesize userID;
#synthesize avatar;
#synthesize firstname;
-(User *)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self)
{
self.userID = [dictionary objectForKey:#"IdUser"];
self.avatar = [dictionary objectForKey:#"Avatar"];
self.firstname = [dictionary objectForKey:#"FirstName"];
}
return self;
}
#end
in my .pch file
#define AppDelegateInstance ((AppDelegate *)[UIApplication sharedApplication].delegate)
then i got the all key and value like this
AppDelegateInstance.loggedUser = [[User alloc] initWithDictionary:[tempArray objectAtIndex:0]];
Response == (
{
IdUser = 1;
Avatar = "Nishant_1.jpg
FirstName = Nishant;
}
)
Now my question is How to update
{
Avatar = "Nishant_1.jpg(Not update **Nishant_1.jpg** to **xyz.jpg**)
FirstName = Nishant(Not Update **Nishant** to **xyz**);
}
If anybody know this plz give me some answer to solve my query
Thanks in Advanced!!!
- (void)updateWithDictionary:(NSDictionary *)dictionary
{
id userId = [dictionary objectForKey:#"IdUser"] ;
if (userId) {
self.userID = userId ;
}
id avatar = [dictionary objectForKey:#"Avatar"] ;
if (avatar) {
self.avatar = avatar ;
}
id firstname = [dictionary objectForKey:#"FirstName"];
if (firstname) {
self.firstname = firstname ;
}
}
Why don't you write a method to update your properties like
-(User *)updateWithDictionary:(NSDictionary *)dictionary
{
if (self)
{
self.userID = [dictionary objectForKey:#"IdUser"];
self.avatar = [dictionary objectForKey:#"Avatar"];
self.firstname = [dictionary objectForKey:#"FirstName"];
}

Trying to filter NSMutableArray with NSPredicate and getting error

I'm trying to filter the results of a list of employee contacts that I keep in my app but am getting the following error : 'Can't use in/contains operator with collection LAST (not a collection)'
I've tried several variations with the NSPredicate command, self, self.last, employee.last, last == 'smith' (this one doesn't generate an error but doesn't return any results).
NSMutableArray *employeesList = [[NSMutableArray alloc] init];
Person2 *employee = [[Person2 alloc] init];
employee.first = #"bob";
employee.last = #"black";
[employeesList addObject:employee];
employee = [[Person2 alloc] init];
employee.first = #"jack";
employee.last = #"brown";
[employeesList addObject:employee];
employee = [[Person2 alloc] init];
employee.first = #"george";
employee.last = #"smith";
[employeesList addObject:employee];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"last contains[cd] %#", #"black"];
NSArray *filteredKeys = [employeesList filteredArrayUsingPredicate:predicate];
NSLog(#"filtered : %#",filteredKeys);
[person2.h]
#interface Person2 : NSObject
{
#private
}
#property (nonatomic, retain) NSString *first;
#property (nonatomic, retain) NSString *last;
+ (Person2 *)personWithFirst:(NSString *)first andLast:(NSString *)last;
#end
[person2.m]
#import "Person2.h"
#implementation Person2
#synthesize first, last;
- (id)init {
self = [super init];
if (self) {
}
return self;
}
+ (Person2 *)personWithFirst:(NSString *)first andLast:(NSString *)last {
Person2 *person = [[Person2 alloc] init];
[person setFirst:first];
[person setLast:last];
return person;
}
- (NSString *)description {
return [NSString stringWithFormat:#"%# %#", [self first], [self last]];
}
#end
I've got an NSArray category that does this sort of thing easily:
#interface NSArray (FilterAdditions)
- (NSArray *)filterObjectsUsingBlock:(BOOL (^)(id obj, NSUInteger idx))block;
#end
#implementation NSArray (FilterAdditions)
- (NSArray *)filterObjectsUsingBlock:(BOOL (^)(id, NSUInteger))block {
NSMutableArray *result = [NSMutableArray array];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (block(obj, idx)) {
[result addObject:obj];
}
}];
return result;
}
So you would call it like this:
NSArray *filteredEmployees =
[employeeList filterObjectsUsingBlock:^BOOL(id obj, NSUInteger idx){
return [(Person2 *)obj.last isEqualToString:#"black"];
}];
So I found the answer and now I feel very dumb!! The use of "first" and "last" are reserved names and cannot be used. I changed the variables to firstName and lastName and it works perfectly.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html.
Thank you all for your comments and assistance.

Resources