Exclude duplicate values for NSMutableArray from Coredata entities - ios

I have a method that returns an NSMutableArray of entities from my CoreData database. The entities look like this
#property (nonatomic, retain) NSNumber * iD;
#property (nonatomic, retain) NSString * number;
#property (nonatomic, retain) ManufacturerNumber *manufacturerNumber;
I need to create a unique array of manufacturerNumber entities based of the number NSString.
this is how my method that returns the current array with duplicates looks.
- (NSMutableArray *)readNumber
{
NSManagedObjectContext *context = [self managedObjectContext];
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ManufacturerNumber" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *manufacturerNumbersDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ManufacturerNumber *manufacturerNumber in fetchedObjects) {
[manufacturerNumbersDictionaryArray addObject:manufacturerNumber];
}
return manufacturerNumbersDictionaryArray;
}
This returns an NSMutableArray including duplicates, and I'd like to remove the duplicates.
Update to question
I have now decided to edit the array when I go to display the values in my UITableview, below I explain what the array contains etc.
I have a NSMutableArray that contains the coredata entities described above, I would like to know how to create a NSSet of unique values from the NSMutablerArray based from the entities cNumber attribute.
This is how I have created the NSMutableArray
tableViewMArray = [NSMutableArray arrayWithArray:[cardManufacturerNumber.cNumbers allObjects]];
As you can see cardManufacturerNumber is a coredata object with a one to many relationship with cNumbers.
cNumbers has 3 attributes
numString
numID
parentObj
I would like to know how to create a unique NSMutableArray based off cNumbers numString attribute.
The NSMutableArray should consisit of unique cNumbers coredata objects. not just numString strings as I need to know the other values.

Have you tried using NSMutableSet instead of NSMutableArray? If you properly implement - (NSUInteger)hash and - (BOOL)isEqual:(id)anObject in a way that makes logical sense on your ManufacturerNumber class this should give you the result you want (this is necessary since sets use these methods to ensure the uniqueness of each object. How you implement these depends on the internals of your ManufacturerNumber class). Or, if you care about order but want to maintain uniqueness, you could use NSMutableOrderedSet.

Unless I am missing the point, this should help for the first part of your question...
Regarding your method (NSMutableArray *)readNumber, try this instead...
- (NSSet *)readNumber
{
NSSet *setReturned = nil;
NSManagedObjectContext *context = [self managedObjectContext];
// Test listing all FailedBankInfos from the store
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ManufacturerNumber" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *manufacturerNumbersDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ManufacturerNumber *manufacturerNumber in fetchedObjects) {
[manufacturerNumbersDictionaryArray addObject:manufacturerNumber];
}
setReturned = [NSSet setWithArray: manufacturerNumbersDictionaryArray];
return setReturned;
}
This should return a discrete set of manufacturerNumber without duplicates.
For the second and updated part of your question, I'm struggling to understand exactly what you need, so forgive me if I am off the mark.
Have you attempted an NSFetchRequest with an NSPredicate, instead of the setting the variable tableViewMArray?
For example...
NSString *key = #"cNumbers";
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"cNumber"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K != %d", key, cardManufacturerNumber.cNumbers];
[fetchRequest setPredicate:predicate];
NSArray *array = [<<insert local managedObjectContext>> executeFetchRequest:fetchRequest error:nil];
NSSet *set = [NSSet setWithArray:array];
and then to make the array mutable...
(with thanks to this SO Q&A)
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:[set allObjects]];

Related

Core Data Fetch doesn't return the object it should

In Core Data, I have a Property entity, with an attribute currentPage of type Integer 16. It is associated with a class ATProperty with a scalar property:
#interface ATProperty : NSManagedObject
#property(nonatomic, assign) int16_t currentPage;
#end
#implementation ATProperty
#dynamic currentPage;
#end
Now I have the following code to perform a request:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Property"];
request.predicate = [NSPredicate predicateWithFormat:#"currentPage != 0"];
NSArray *results = [context executeFetchRequest:request error:nil];
Most of the times, it works as intended. But sometimes, I get the following weird results:
(lldb)po results
<__NSArrayI 0x17e61300>(
)
(lldb)po [context executeFetchRequest:[NSFetchRequest fetchRequestWithEntityName:#"Property"] error:nil]
<_PFArray 0x17e9f3e0>(
<ATProperty: 0x17ec6400> (entity: Property; id: 0x19178e20 <x-coredata:///Property/t88822504-5974-42F5-B8D2-A62325064AFF28> ; data: {
currentPage = 9;
})
)
(lldb)p (BOOL)[request.predicate evaluateWithObject:0x17ec6400]
(BOOL) $12 = YES
So there is one instance of the entity in the managed object context. When I execute my fetch request, Core Data doesn't return this object, it returns an empty array. However, if I test the same predicate on the object that exists in the context, the predicate evaluates to YES. Which means the object should have been in the results. How is that possible?
Can you please try your code simulation with below. It's really work for me and also helps us to restrict from crash and other result issues.
NSManagedObjectContext *managedObjectContext;
NSEntityDescription *entityDesc = [NSEntityDescription
entityForName:#"Property"
inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred =
[NSPredicate predicateWithFormat:#"currentPage != 0"];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request
error:&error];
if ([objects count] > 0) {
NSLog();
}
Hope it helps. Please let me know if we have to go with another solution.

NSManagedObject fault / cannot get 'primitive' object handled by NSManagedObject

I am struggling with CoreData. I'd like to fetch the following object:
#interface Email : NSManagedObject {
}
#property (nonatomic, copy) NSString *email;
#property (nonatomic, copy) NSString *contact;
#end
..and put the result inside a NSMutableArray, but the NSString contents, (not NSManagedObjects!). This is because I am using json-framework and that engine does not allow NSManagedObjects to be passed.
These lines fetch perfom the fetch from CoreData
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Emails" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:#"email"]];
NSError *error = nil;
NSMutableArray *fetchedObjects = [[NSMutableArray alloc] init];
fetchedObjects= [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
flight.emails=fetchedObjects;
The 'flight' object is declared as follows:
#interface Flight : NSObject {
NSMutableArray *_emails;
}
#property (nonatomic, retain) NSMutableArray *emails;
With this code I am getting CoreData faults. I tried some other implementation variants, but never actually managed to have NSString objects in flight.emails, but always NSManagedObjects. What I tried was to implement a dedicated getter function in the Email NSManagedObject that copies the fetched NSString and returns the copy.
I get the idea that this is kind of a common problem, however, my research has not led to a solution on this one here.
Thanks,
Peter
From experience, setPropertiesToFetch: only works when requesting the returned objects be dictionaries. So a couple of problems in your code:
You are asking for specific property (email), but have not set the return type to dictionary. Take a look at setResultType:.
You still need to take the result and extract the email objects from it. You cannot just assign the resulting array to your emails property.
Try this:
[fetchedRequest setResultType:NSDictionaryResultType];
NSArray* results = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if(error != nil || results == nil) return;
flight.emails = [results valueForKeyPath:#"email"];

fetch coredata relationship

I would like to know how to fetch the items from my coredata relation ship. I guess it should be a dictionary or arrays or something that gets returned so that you can have your one to many thing.
I am quite lost at this I know how to write/read single objects but this relationship stuff is abit confusing.
I think I have sucsessfully written a relationship to coredata however now I would like to be able to read it to see if I have it right.. I have started writing the method for this but have no idea what to actually do to get all of the information out.
this is the code i have so far.. for both read and write
- (void)writeFin:(NSArray *)recivedProjectData ItemsData:(NSArray *)itemsData {
// WRITE TO CORE DATA
NSManagedObjectContext *context = [self managedObjectContext];
for (NSDictionary *dict in recivedProjectData) {
project = [NSEntityDescription insertNewObjectForEntityForName:#"Project" inManagedObjectContext:context];
project.proNumber = [dict valueForKey:#"ProNumber"];
project.projectDescription = [dict valueForKey:#"Description"];
// project.items = [dict valueForKey:#""]; // this is the relationship for project
}
for (NSDictionary *dict in itemsData) {
items = [NSEntityDescription insertNewObjectForEntityForName:#"Items" inManagedObjectContext:context];
items.Number = [dict valueForKey:#"Number"];
items.Description = [dict valueForKey:#"Description"];
items.comment = [dict valueForKey:#"Comment"];
items.project = project; // this is the relationship for items
[project addItemsObject:items];
}
NSError *error = nil;
if (![__managedObjectContext save:&error]) {
NSLog(#"There was an error! %#", error);
}
else {
NSLog(#"created");
}
}
- (NSMutableArray *)readFin {
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Project" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *projectDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (ProjectList *projectList in fetchedObjects) {
NSMutableDictionary *tempProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempProjectDictionaryArray setObject:project.proNumber forKey:#"ProNumber"];
[tempProjectDictionaryArray setObject:project.description forKey:#"Description"];
[tempProjectDictionaryArray setObject:project.projectID forKey:#"ProjectID"];
[projectDictionaryArray addObject:tempProjectDictionaryArray];
}
return projectDictionaryArray;
}
So just o reiterate, I would like to know A, is my write method look okay? B, how do you fetch/read the relationship objects from core data.
any help would be greatly appreciated.
A relationship in Core Data isn't an object, its a property which happens to correspond to another object in your model rather than just being a dead end. You're already most of the way there as far as checking whether your relationships are ok as far as I can see -- what you need to do is add one more line in your projectList
[tempProjectDictionaryArray setObject: project.items forKey:#"items"];
the object that you will have added will be an NSSet. You can then check that things are as they should be with a loop like this after you've finished setting things up
NSSet itemsForProject = projectDictionaryArray[someIndex][#"items"]
for (Item* currItem in [itemsForProject allObjects]) {
//access some property of the current item to make sure you have the right ones -- \
description for example
NSLog(#"%#", item.description);
}

Core Data how to retrieve a column from an entity

I am using Core Data for my ios app and I am wondering how would I go about in retrieving an entire column from an entity table? For example I am soly interested in grabbing the primary key from my table.
In sql i would just do Select name from MYTABLE.
I think you could do it this way :
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"--table--" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
NSMutableArray *elementsFromColumn = [[NSMutableArray alloc] init];
for (NSManagedObject *fetchedObject in fetchedObjects) {
[elementsFromColumn addObject:[fetchedObject valueForKey:#"--column--"]];
}
So you have all the elements from a specific column of your table.
Hope it's what you're looking for :)
Look at the documentation for NSFetchRequest. You can ask it to return dictionaries containing specific properties only - this is about as close as you will get. The methods of interest are setResultType: and setPropertiesToFetch:.

Core Data returning NSArrays instead of NSStrings

For some reason all of the NSString typed attributes are being returned as NSArrays in my Article object. Here's my function to retrieve them:- (NSArray *)getSavedArticles
{
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSError *error = nil;
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Article" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *dateSort = [NSSortDescriptor sortDescriptorWithKey:#"last_opened" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:dateSort]];
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
return fetchedObjects;
}
I have not created a NSManagedObjectModel for these, and instead I'm just accessing them using KVO.
//self.data has the array returned from getSavedArticles
NSManagedObject *object = [self.data objectAtIndex:indexPath.row];
NSString *path = [object valueForKey:#"path"];
NSString* id = [object valueForKey:#"id"];
When I look at this in the variables pane, on path and id I see (__NSArrayI *) ... Variable is not a CFString. Printing the description of either of these also prints out the parenthesis used when printing arrays.
I'm just wondering if this could be due to the sort descriptor? I have double checked that the data going into these objects is typed correctly, and the SQLite database that I'm using to backup everything is displaying strings.
I have tried re-installing the app in the simulator, and resetting it. I still get a NSArray instead of a NSString.
I really don't know what's going on here. Any help would be appreciated.
EDIT: I just found something else interesting. I do another check to see if an Article has been saved, and this time I don't get a NSArray for the article path:- (NSString *)hasArticleSaved:(NSString *)id
{
NSString *path = nil;
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"id", id]];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Article" inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects.count > 0) {
NSManagedObject *savedArticle = [fetchedObjects objectAtIndex:0];
path = [savedArticle valueForKey:#"path"];
}
return path;
}
Now I'm really confused. Any takers?
Your problem is here:
[self.data objectForKey:indexPath.row]
...because objectForKey: is looking for a string key name e.g. "path" or "id". Giving it an integer will produce an error or a random return.
Instead, you want:
[self.data objectAtIndex:indexPath.row]
... which will return the managed object at the given index in the array.
I found the issue. I had changed the data structure for my table so self.data is an array of arrays...and with only 1 object it looked like I was getting a NSArray back when in fact I was just accessing the data wrong. I just needed to do [[self.data objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

Resources