Compare the contents of two arrays and finding the differences - ios

I have two arrays, one is an array from a fetch request of core-data (array1), the other is from data that is being pulled from the web(array2). What I want to do is compare array1 to array2 and any items that are in array2 that are not in array1 need to be added to core-data.
The data I'm pulling from has id's associated with each person. When I create a new "person" entity I save this id with it as well. I'm not sure how to compare the arrays using the Person's id, or even how to access that within an array.
Here is the fetch request:
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSError *error = nil;
[fetch setEntity:[NSEntityDescription entityForName:#"Person" inManagedObjectContext:self.managedObjectContext]];
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:#"Pid" ascending:YES];
request.sortDescriptors = #[sorter];
NSArray *items = [self.managedObjectContext executeFetchRequest:request error:&error];
I have the fetch sorted in the same way array2 with the new data is sorted. I'm just not sure how to compare the two and then add the new items into core-data. Please help?
Update:
NSDictionary *qDict = [JSON objectForKey:#"person"];
NSArray *qArry = [JSON objectForKey:#"person"];
//Used to print the id's
_testArray = [NSMutableArray arrayWithArray:[qDict valueForKey:#"id"]];
for (NSNumber *numb in _testArray) {
NSLog(#"id = %#", numb);
}
NSError *error = nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Person"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"Pid IN %#", [qDict valueForKey:#"Pid"]];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"Pid" ascending:YES]]];
NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSLog(#"%d how many items from the fetch", items.count);
for (NSDictionary *qdt in qArry) {
NSUInteger currentIndex = 0;
Person *q = nil;
if ([items count] > currentIndex) {
q = [items objectAtIndex:currentIndex];
}
if ([q.Pid integerValue] == [[qdt objectForKey:#"id"] integerValue]) {
// Either update the object or just move on
}
else {
// Add the object to core data
q = [NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:self.managedObjectContext];
q.url = [qdt valueForKey:#"url"];
q.Pid = [qdt objectForKey:#"id"];
q.text = [qdt valueForKey:#"personText"];
NSError *error = nil;
[_managedObjectContext save:&error];
}
currentIndex++;
}
//[_managedObjectContext save:&error];
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Error retrieving data" message:#"Please try again" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[av show];
}];

First you can fetch the list of objects stored in your core data based on person's IDs that you retrieve from the web (idArray) and sort them based on the ID:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Person"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id IN %#", idArray];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"id" ascending:YES]]];
This will return you the list of objects that the id can also be found in the objects you retrieve from the web. Let us call this storedRecords array. Then you can do the following:
Set a counter,
Iterate over the downloadedArray (this is the array containing the objects retrieved from the web and it must be sorted by id too),
Use objectAtIndex in the storedRecords array,
Check the id of the storedManagedObject if it matches the id of the record object
if it does not match, it is a new object that you can save into core data. Otherwise, it is an existing object.
Increase the counter.
Here is an example:
int currentIndex = 0;
for (NSDictionary *record in downloadedArray) {
NSManagedObject *storedManagedObject = nil;
if ([storedRecords count] > currentIndex) {
storedManagedObject = [storedRecords objectAtIndex:currentIndex];
}
if ([[storedManagedObject valueForKey:#"id"] integerValue] == [[record valueForKey:#"id"] integerValue]) {
//this will be existing object in core data
//you can update the object if you want
} else {
//this will be new object that you can store into core data
}
currentIndex++;
}
This is similar to what is mentioned here:
http://www.raywenderlich.com/15916/how-to-synchronize-core-data-with-a-web-service-part-1

maybe you can do something like this:
NSMutableArray *webData = [NSMutableArray arrayWithObjects:#"object 1",#"object 2",#"object 3",#"object 4", nil];
NSMutableArray *coreData = [NSMutableArray arrayWithObjects:#"object 2",#"object 4", nil];
NSMutableArray *newData = [NSMutableArray arrayWithArray:webData];
[newData removeObjectsInArray:coreData];
//output "object 1", "object 3"
or if you have a custom NSObject inside the array and compare it with its primary key maybe you can do something like:
NSMutableArray *webData = [NSMutableArray arrayWithObjects:test1,test2,test3,test4, nil];
NSMutableArray *coreData = [NSMutableArray arrayWithObjects:test2,test3, nil];
__block NSMutableArray *newData = [NSMutableArray new];
[webData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
__block testObject *object = (testObject *)obj;
__block BOOL isExisting = NO;
[coreData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
testObject *innerObject = (testObject *)obj;
if(object._id == innerObject._id)
isExisting = YES;
}];
if(!isExisting)
[newData addObject:object];
}];
//output will be object 1 and object 4

Try this
NSMutableArray *array1 = [[NSMutableArray alloc] initWithObjects:#"a",#"a",#"a",#"d",#"b",#"b",#"c",#"a",#"c",nil];
NSMutableArray *array2 = [[NSMutableArray alloc] initWithObjects:[NSString stringWithFormat:#"%#",[array1 objectAtIndex:0]], nil];;
for(int i = 0;i<[array1 count];i++)
{
if ([array2 containsObject:[array1 objectAtIndex:i]]) {
NSLog(#"do nothing");
}
else{
[array2 addObject:[array1 objectAtIndex:i]];
NSLog(#"array2 is %#",array2);
}
NSLog(#"finally array2 is %#",array2);
}
array2 will be as { a,d,b,c }

Related

Delete record from core data not working?

I am working with core data, when I deleting record from DB it's not working.
Entity name : Entity
Attributes : id, date, title
- (void) getData {
NSFetchRequest * fetReq = [NSFetchRequest fetchRequestWithEntityName:#"Entity"];
NSError * fetchErrorObj;
NSArray *result = [self.ad.managedObjectContext executeFetchRequest:fetReq error:&fetchErrorObj];
NSLog(#"array count is %lu", result.count);
NSMutableArray *idArr = [[NSMutableArray alloc]init];
NSMutableArray *titleArr = [[NSMutableArray alloc]init];
NSMutableArray *dateArr = [[NSMutableArray alloc]init];
for (int i=0; i<result.count; i++) {
self.storedManagedObj = [result objectAtIndex:i];
[idArr addObject:[self.storedManagedObj valueForKey:#"id"]];
[titleArr addObject:[self.storedManagedObj valueForKey:#"title"]];
[dateArr addObject:[self.storedManagedObj valueForKey:#"date"]];
}
self.idArray = sidArr;
}
To delete record...
- (IBAction)deleteRecord:(UIButton *)sender {
NSNumber *value=[NSNumber numberWithInt:[[self.idArray objectAtIndex:0] intValue]];
NSLog(#"%#", [self.idArray objectAtIndex:0]);
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Entity"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", value];
NSLog(#"predicate :%#", predicate);
[request setPredicate:predicate];
NSLog(#"request :%#", request);
NSError *error = nil;
NSArray *result = [self.ad.managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"result: %#", result);
if (!error && result.count > 0) {
for(NSManagedObject *managedObject in result){
NSLog(#"managedObject :%#", managedObject);
[self.ad.managedObjectContext deleteObject:managedObject];
}
//Save context to write to store
[self.ad.managedObjectContext save:nil];
}
}
I am getting result like this
predicate :id == 38
request : (entity: Entity; predicate: (id == 38); sortDescriptors: ((null)); type: NSManagedObjectResultType; )
result :(
)
The error is pretty clear. It states that you need to specify SortDescriptor to your FetchRequest
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Entity"];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"<#Sort key#>"
ascending:YES];
request.sortDescriptors = #[sortDescriptor];
OR
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
Read : NSFetchRequest without sort descriptors
EDIT:
As OP isn't concerned about sorting the result in any specific order and the question lacks the description of entity and the only field that I can see in question is id updating my answer to use id in sort descriptor field
[[NSSortDescriptor alloc] initWithKey:#"id" ascending:YES];
EDIT 2:
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Entity"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"id == %#", [self.idArray objectAtIndex:0]];
Issue in the code was :
NSNumber *value=[NSNumber numberWithInt:[[self.idArray objectAtIndex:0] intValue]];
OP was trying to create a NSNumber from an object in self.idArray using intValue which means idArray is a array of String and not NSNumber. That means id is saved as String in core data and not as NSNumber.
In predicate however OP was trying to pass NSNumber to compare with id. Since id is String and passed argument is NSNumber comparison was failing hence was not returning any objects.

How to get Model class object values while typeCasting it in nsarray?

I typeCast Product(name of a model class where objects is created) class with array (object of NSArray which contains fetched data from database).When I want to set values of project class object into NSMutableDictionary i.e [data setObject:product.prodName forKey:kProductName]; app crashes.
entityDescription = [NSEntityDescription entityForName:#"Product" inManagedObjectContext:appDelegate.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSSortDescriptor *sd = [[NSSortDescriptor alloc] initWithKey:#"prodId" ascending:NO];
request.sortDescriptors = #[sd];
[request setPredicate: [NSPredicate predicateWithFormat:#"active = %# AND isDeletedProd = %#",#1,#0]];
_fetchedResultController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:appDelegate.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
_fetchedResultController.delegate = self;
request.resultType = NSDictionaryResultType;
NSError *error;
[_fetchedResultController performFetch:&error];
arrdata = [self.fetchedResultController fetchedObjects];
This code i used to fetch data.And then want to typecast it into Product.
Product *arrProd = (Product *)arrdata;
Then Pass arrProd into json by using method given below.
[[ServerSyncUtil sharedInstance] syncProduct:arrProd withHandler:^(BOOL success,NSData *data , NSError *error)
{
}];
Which all this method..
-(void)syncProduct:(Product *)product withHandler:(syncHandler)handler {
NSMutableArray *products = [NSMutableArray array];
[products addObject:[self addProduct:product]];
[self uploadProducts:products withHandler:handler];
}
And then addProduct method called.
-(NSDictionary *) addProduct:(Product *) product {
NSMutableDictionary *data = [NSMutableDictionary dictionary];
NSLog(#"%#",product);
[data setObject:product.prodName forKey:kProductName];
[data setObject:product.desc ? product.desc:#"" forKey:kDescription];
return datal
}
Here when want to setObject into array data app goes crash.
You already have an array, you don't need to create a new one. You're trying to treat an array as a single object. You even call the variable arrdata indicating you know it's an array, but you don't treat it as an array.
Instead use:
NSArray *products = [self.fetchedResultController fetchedObjects];

Issue with Coredata update

I'm trying to update certain currency abbreviations in the coredata with this function.
- (void)updateCurrencies
{
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Transaction" inManagedObjectContext:managedObjectContext]];
NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"Number of data : %lu", (unsigned long)[results count]);
for (int i = 0; i < [results count]; i++) {
Transaction* t = [results objectAtIndex:0];
NSLog(#"currency: %#", t.currency);
if ([t.currency isEqualToString:#"CAN"]) {
t.currency = #"CAD";
NSLog(#"new currency set.");
}
[self saveContext];
}
}
}
I call this function in didFinishLaunchingWithOptions. Now, the log does inform me that t.currency has been updated to CAD. However when I retrieve the data again in HomeViewController and log the currency, it is back to CAN. This is the code in HomeViewController,
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *transaction = [NSEntityDescription entityForName:#"Transaction" inManagedObjectContext:_managedObjectContext];
[request setEntity:transaction];
request.predicate = [NSPredicate predicateWithFormat:#"transactionToUser = %#", [self.content objectAtIndex:i]];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"postdate" ascending:NO];
NSArray *descriptors = [[NSArray alloc] initWithObjects:descriptor, nil];
[request setSortDescriptors:descriptors];
NSError *error = nil;
NSMutableArray *mutableResult = [[_managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableResult == nil) {
//handle error
}
for (int k = 0; k < [mutableResult count]; k++) {
Transaction *t = [mutableResult objectAtIndex:k];
NSLog(#"currency xx: %#", t.currency);
}
What am I doing wrong? Any help is appreciated. Thanks.
Fixed it with a different for loop.
for (Transaction *t in results)
{
...
}

Deleting objects in core data depending on JSON

I have a NSManagedObjectModel with an entity, "Project". I get all the projects to show them in a UITableView with NSFetchedResult controller. Now if the JSON has new Projects I insert them and if the JSON has updated items I update the items in the Core Data context.
So, my problem is when I get a JSON with less items than the context. I have thought about two ways to delete items in my context. One way is Delete all the context and save it again with the new items. The other way is create an array with all the items in the context and check it with the items in the JSON by id and if there is not one item, then remove it.
I have the idea, but I don't know which is the best way. I have thought also in a backgroundContext.
I use this method right now with out deleting methods:
#pragma mark - Project List service
- (void)getProjectListWithCpompletionBlock:(CompletionBlock)completionBlock{
NSMutableURLRequest *request = [self requestWithMethod:#"GET" path:kAPIProjectList parameters:nil];
[request setTimeoutInterval:kTimeOutRequest];
AFJSONRequestOperation *requestOperation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSDictionary *projects = [JSON valueForKey:kTagProjects];
for (NSDictionary *projectDic in projects) {
Project *pro = [Project getProjectWithId: [projectDic objectForKey:kTagProjectId] ];
if (pro) {
[Project updateProjectWithDictionary:projectDic];
NSLog(#"update %# ",[projectDic objectForKey:kTagProjectId]);
} else {
[Project createProjectWithDictionary: projectDic];
NSLog(#"create %# ",[projectDic objectForKey:kTagProjectId]);
}
}
[ypCoreDataManager saveContext];
if (completionBlock) {
completionBlock(NO, nil);
}
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *aError, id JSON) {
NSLog(#"%# Failure JSNON Error%#", NSStringFromSelector(_cmd), aError);
if (completionBlock) {
completionBlock(YES, aError);
}
}];
[self enqueueHTTPRequestOperation:requestOperation];
}
Project+Helper is my project category and here it is the code.
+ (Project *)createProjectWithDictionary:(NSDictionary *)dic {
Project *project = nil;
project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:mainContext];
project.projectId = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectId] intValue]];
project.title = [[dic valueForKey:kTagProjectTitle]description];
project.estimatedPrice = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectEstimatedPrice] floatValue]];
NSMutableArray *tags = [[NSMutableArray alloc] init];
tags = [dic objectForKey:kTagProjectsTags];
NSMutableSet *tagSet = [[NSMutableSet alloc]init];
for (NSDictionary * tagDic in tags){
NSString *tagName = [tagDic objectForKey:kTagProjectTagName];
Tag *tag = [Tag insertTagName:tagName inManagedObjectContext:mainContext];
[tagSet addObject:tag];
}
[project addTags:tagSet];
return project;
}
// Return project by id
+ (Project *)getProjectWithId:(NSString *) projectId {
Project *project = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"projectId" ascending:YES]];
request.predicate = [NSPredicate predicateWithFormat:#"projectId = %#", [projectId description]];
// Execute the fetch
NSError *error = nil;
NSArray *matches = [mainContext executeFetchRequest:request error:&error];
if (!matches || ([matches count] > 1)) { // nil means fetch failed; more than one impossible (unique!)
// handle error
} else { // found the Project, just return it from the list of matches (which there will only be one of)
project = [matches lastObject];
}
return project;
}
// Update project
+ (Project *)updateProjectWithDictionary:(NSDictionary *)dic {
Project *project = nil;
// Build a fetch request to see if we can find this Project in the database.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES]];
request.predicate = [NSPredicate predicateWithFormat:#"projectId = %#", [dic[kTagProjectId]description]];
// Execute the fetch
NSError *error = nil;
NSArray *matches = [mainContext executeFetchRequest:request error:&error];
// Check what happened in the fetch
if (!matches || ([matches count] > 1)) { // nil means fetch failed; more than one impossible (unique!)
// handle error
} else {
project = [matches lastObject];
project.projectId = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectId] intValue]];
project.title = [[dic valueForKey:kTagProjectTitle]description];
project.estimatedPrice = [NSNumber numberWithInt:[[dic valueForKey:kTagProjectEstimatedPrice] floatValue]];
NSMutableArray *tags = [[NSMutableArray alloc] init];
tags = [dic objectForKey:kTagProjectsTags];
NSMutableSet *tagSet = [[NSMutableSet alloc]init];
for (NSDictionary * tagDic in tags){
NSString *tagName = [tagDic objectForKey:kTagProjectTagName];
Tag *tag = [Tag insertTagName:tagName inManagedObjectContext:mainContext];
[tagSet addObject:tag];
}
[project addTags:tagSet];
}
return project;
}
You have to add this method in your Project's category and in your code after you add the new item call this method where you pass you array objects living in the Core Data and it remove all the objects more that you haven't in the array
+(void)removeExpiredProjectBy:(NSMutableArray *)ProjectLiving inContext:(NSManagedObjectContext *)context{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Project"];
if (projectLiving.count) {
request.predicate = [NSPredicate predicateWithFormat:#"NOT (projectId IN %#)", [projectLiving copy]];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (matches.count != 0) {
for (Project *pro in matches) {
[context deleteObject:pro];
}
}
}
}
As you process the JSON you can build a list of all of the ids that have been added / updated. Then, after that is complete you can create a fetch request with a predicate which finds all items where NOT (id IN %#) and supply the list of ids. This will return you only the items that need to be deleted.
Or, from an efficient API point of view, the server should give you a deletion list because it doesn't need to confirm items that haven't changed if you send it a 'last request date'...

coredata - fetch one attribute into an array

Aim: I would like to fetch the value of one attribute (from an entity) from the database (core data) into an array.
Example
Entity Name = Employees
Attribute = employeeID
I just want all the employeeIDs populated into an array / set.
Question
Given below is my implementation, I just feel it is kind of a round about way, I would like to know if there is a better way to do this.
Code
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Employees"];
fetchRequest.resultType = NSDictionaryResultType;
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObjects:#"employeeID", nil]];
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest
error:&error];
NSMutableArray *employeeIDs = [NSMutableArray array];
for(id currentRecord in results)
{
[employeeIDs addObject:[currentRecord objectForKey:#"employeeID"]];
}
You can avoid the last for loop,
Instead of,
NSMutableArray *employeeIDs = [NSMutableArray array];
for(id currentRecord in results)
{
[employeeIDs addObject:[currentRecord objectForKey:#"employeeID"]];
}
Try this,
NSMutableArray *employeeIDs = [results valueForKey:#"employeeID"];
One way of doing it is-
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Employees"];
fetchRequest.resultType = NSDictionaryResultType;
NSError *error = nil;
NSArray *results = [self.managedObjectContext executeFetchRequest:fetchRequest
error:&error];
NSMutableArray *employeeIDs = [results valueForKey:#"employeeID"];

Resources