I am developing an application where i used core data framework for the purpose of maintaining a database. My entity contains three attributes called: name, start time and end time of a list of applications. I am getting the correct values for name and start time attribute.
Now my problem is my end time attribute should contain the value of the next entries start time value. If anybody having any idea about this please let me know.
Thanks
You can leave the endTime attribute blank until you create the next entity. In the +Create category on the entity, get the last/first object (assuming you are using ordered entities) and update the endTime with the same value used for the new startTime.
If your objects are not ordered it could be a bit tricky since all the entities are in a set. But if ordered, you are good since NSOrderedSet responds to lastObject (and firstObject).
Enjoy,
Damien
EDIT: Here is an example factory method that either 1) returns the existing stock entity for a stock symbol or 2) creates a new entity for that symbol. Pretty easily modified to get entities and select the first/last depending on your sort order. Again see the Core Data classes from Prof. Hegarty.
+ (Stock *)stockForSymbol:(NSString *)symbol inManagedObjectContext:(NSManagedObjectContext *)context {
Stock *stock = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Stock"];
request.predicate = [NSPredicate predicateWithFormat:#"symbol = %#",symbol];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"symbol" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
if (!matches || [matches count] > 1) {
// handle error
} else if ([matches count] == 0) {
stock = [NSEntityDescription insertNewObjectForEntityForName:#"Stock" inManagedObjectContext:context];
stock.symbol = symbol;
stock.strategyPosition = [NSNumber numberWithInt:StrategyPositionFlat];
stock.userPosition = stock.strategyPosition;
stock.userOwns = [NSNumber numberWithBool:NO];
} else {
stock = [matches lastObject];
}
return stock;
}
Related
I have two entities, one called InProject that has several attributes and one relationship. the relationship is with another entity called Ins.
I am editing one of the Ins that is related to InProject. I used InProject attribute ID which then returns a NSDictionary value that has several key-values one of which is for an array of Ins. I then find the Ins I need to edit in a for loop I edit them, but then I become unstuck because I am not sure how to save the contect of InProject with the *updated Ins
I need to figure out how to save InProject after I have overwritten the Ins attributes I need to update.
This is what my code looks like after battling this problem:
- (void)editSelectedins:(NSString *)projIDString UpdatedNSD:(NSMutableDictionary *)updatedNSD DPC:(int)dpc{
// get context
NSManagedObjectContext *context = [self managedObjectContext];
if (context == nil) {
NSLog(#"Nil");
}
else {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"InsProject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
NSMutableArray *InsProjectDictionaryArray = [[NSMutableArray alloc] init];
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (InsProject *insProj in fetchedObjects) {
NSMutableDictionary *tempInsProjectDictionaryArray = [[ NSMutableDictionary alloc] init];
[tempInsProjectDictionaryArray setObject:insProj.companyName forKey:#"CompanyName"];
[tempInsProjectDictionaryArray setObject:insProj.projNo forKey:#"ProjNo"];
[tempInsProjectDictionaryArray setObject:insProj.desc forKey:#"Desc"];
[tempInsProjectDictionaryArray setObject:insProj.guid forKey:#"GUID"];
[tempInsProjectDictionaryArray setObject:insProj.projID forKey:#"ProjID"];
[tempInsProjectDictionaryArray setObject:insProj.ins forKey:#"ins"];
[InsProjectDictionaryArray addObject:tempInsProjectDictionaryArray];
}
// now that you have the InsProjects, choose the one you are curently working on in insView using the projectID
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ProjID==%#",projIDString];
[fetchRequest setPredicate:predicate];
// new array with one value that was created using the NSPredicate ProjID
NSArray *tempInsProjectArray = [InsProjectDictionaryArray filteredArrayUsingPredicate:predicate];
// get ins array out of the NSDictionary to edit
NSSet *inssForInsProject = tempInsProjectArray[0][#"ins"];
NSMutableArray *tempAllinss = [[NSMutableArray alloc] init]; // this will contain everything, that means all repeated values are included
for (Items* currItem in [inssForInsProject allObjects]) {
NSArray *keys = [[[currItem entity] attributesByName] allKeys];
NSDictionary *dict = [currItem dictionaryWithValuesForKeys:keys];
[tempAllinss addObject:dict];
}
NSArray *myArray = [tempAllinss copy];
// get the correct items from myArray anything whos dpc matches the dpc parameter of this method
NSMutableArray *editedinsArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [myArray count]; i++) {
NSMutableDictionary *tempinssDictionary = [myArray objectAtIndex:i];
// if you get a match put it into the new editedinsArray to be edited
if ([[tempinssDictionary objectForKey:#"dpc"] integerValue] == dpc) {
[editedinsArray addObject:tempinssDictionary];
}
}
// by now you should have three things
// 1, access to your ins coredata object //this s wrong I actually have access to insProject
// 2, the values you need to be edited saved into a NSArray (editedinsArray, which will be used to check against and keep old values correct)
// 3, UpdatedNSD which will be used to update any values that need to be updated.
// go through your values and update the ins object
int i = 0;
for (ins *temp in editedinsArray) {
NSDictionary *currentEditedins = [editedinsArray objectAtIndex:i];
i++;
// these values should stay the same so use currentEditedins which contains old vals
NSString *stringToNumberDpc = [currentEditedins valueForKey:#"dpc"];
int tempDpcNum = [stringToNumberDpc integerValue];
NSNumber *dpcNumber = [NSNumber numberWithInt:tempDpcNum];
temp.dpc = dpcNumber;
NSString *totDQtyString = [currentEditedins valueForKey:#"totDQty"];
if ((NSNull *)totDQtyString == [NSNull null]) {
temp.totDQty = #"";
} else {
temp.totDQty = totDQtyString;
}
NSString *totShipString = [currentEditedins valueForKey:#"totShip"];
if ((NSNull *)totShipString == [NSNull null]) {
temp.totShip = #"";
} else {
temp.totShip = totShipString;
}
// values to be updated so use updatedNSD wthich was passed in as method param with the new vals
temp.newInsComp = [updatedNSD valueForKey:#"newInsComp"];
temp.newDryComp = [updatedNSD valueForKey:#"newDryComp"];
temp.updatedRow = [updatedNSD valueForKey:#"updatedRow"];
}
#warning --- I have no idea what to do here... i.e. how do I update the tempInsProjectArray.ins values I have just updated in the above for loop then save context which I hope would update insProj and the ins entities involved.
//save
[context save:&error];
}
}
As you can see at the bottom of the code with #warning I explain where I am having the issue. if I log temp inside the for loop I see the updated values perfectly the issue I am having is how do I then update the current tempInsProjectArray.ins values that I have just edited? then save them of course.
Your code is in great need of simplification. Some ground rules:
Use names with smallInitial and camelCase for variables. So not InsProjectDictionaryArray but insProjectDictionaryArray.
The same applies to dictionary keys indicating attribute names of managed objects. So projNo, not ProjNo.
Avoid cryptic abbreviations. Use plain and readable English Not projNo but projectNumber. What is an Ins? What is "dcp"?
Don't use the plural form for entity names. An suitable name for an item is Item, not Items
Don't use the mutable versions of dictionary and array when immutable ones would do.
Avoid duplicating your data, such as in [array copy].
Avoid dictionaries when you have an object graph. The object graph is what core data creates. It renders dictionaries with values and keys unnecessary.
Don't use IDs. The object graph renders those unnecessary as well in most cases. If you use IDs, do not use strings but numbers, such as long ints, or the object version NSNumber.
When fetching data from the Core Data persistent store, don't fetch all the data and the filter the result. Fetch only the data you need.
What you want to accomplish can surely be done in a few lines of code. I will try to summarize what you want to do as far as I understand it.
Your data model looks something like this:
Project <----->> Item
Where the items are in a to-many relationship called ins. I will rename this items. I will also assume that you will refactor your IDs to be of type NSNumber.
All the code up to myArray could be substituted with this:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:"Project"];
request.predicate = [NSPredicate predicateWithFormat:#"projectID = %#", projectID];
request.fetchLimit = 1;
NSArray *fetchedObjects = [self.managedObjectContext
executeFetchRequest:request error:nil];
Project *project = fetchedObjects[0];
You now have all items available simply with project.items. I understand that there could be more than one item with a mysterious attribute dcp of type int (i.e. NSNumber for managed objects), that is equal to the dcp parameter passed.
NSSet *matchingItems = [project.items filteredSetUsingPredicate:
[NSPredicate predicateWithFormat:#"dcp = %#", #(dcp)]];
Now it becomes a bit murky. Why do you have type ins in your for loop if the ins are actually of type Item? You then cast them into a dictionary... This should generate a compiler error. Or you have another class called ins instead of Ins??
Anyway, if you stay with the Items you can just update the values with what you pass in your dictionary:
for (Item *item in matchingItems) {
item.newInsComp = [updatedNSD valueForKey:#"newInsComp"];
item.newDryComp = [updatedNSD valueForKey:#"newDryComp"];
item.updatedRow = [updatedNSD valueForKey:#"updatedRow"];
}
[self.managedObjectContext save:nil];
Done!
BTW you could make it even shorter by setting the entity name of the fetch request to "Item" and setting the following predicate:
[NSPredicate predicateWithFormat:#"project.projectID = %# && dcp = %#",
projectID, #(dcp)];
If you know your InProject, then updating your Ins related to that project is a matter of editing property values on your managed objects.
Why not use the predicate to get an NSManagedObject of the InProject, then pull the relationship off of that and edit the values?
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
return;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"InsProject" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Set the predicate on the Core Data fetch request instead
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"ProjID==%#",projIDString];
NSError *error;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
// We now have an array that has objects matching the projectIdString
// Might want to do some additional checks if you're only expecting zero or one objects
InsProject *aProject = [fetchedObjects lastObject];
// If we have no project, no point going any further
if ( !aProject ) return;
// On this NSManagedObject is an NSSet property with all related Ins objects
for ( Ins *anIns in aProject.ins ) {
// If our Ins item matches the passed dpc...
if ( [ins.dpc integerValue] == dpc ) {
// ...we have a match, edit properties
ins.dpc = #(dpc);
ins.newInsComp = [updatedNSD valueForKey:#"newInsComp"];
ins.newDryComp = [updatedNSD valueForKey:#"newDryComp"];
ins.updatedRow = [updatedNSD valueForKey:#"updatedRow"];
}
}
// These are managed objects, so saving the context saves all the changes
NSError *saveError;
[context save:&saveError];
if ( saveError ) {
NSLog(#"Save error: %#", [error localizedDescription]);
}
This is the Core Data model. Image
DiaCD <--->> HoraCD <<---> ActividadCD
In the entity "Activity" is a category called attribute to filter the activities. My question is: How could I make a query to give me back the days with activities where the category is "X"?
Try one:
NSEntityDescription *entityDescriptionDia = [NSEntityDescription entityForName:#"DiaCD" inManagedObjectContext:managedObjectContext];
NSFetchRequest *requestDia = [[NSFetchRequest alloc] init];
NSPredicate *predicateDia = [NSPredicate predicateWithFormat:#"ANY relDiaHora.relHoraActividad.categoria == %#", categoria];
[requestDia setEntity:entityDescriptionDia];
[requestDia setPredicate:predicateDia];
NSError *errorDia;
NSArray *arrayDia = [managedObjectContext executeFetchRequest:requestDia error:&errorDia];
if ([arrayDia count] > 0) {
for (DiaCD *dia in arrayDia) {
NSSet *setHora = dia.relDiaHora;
HoraCD *horaQuery = [setHora anyObject];
ActividadCD *actividadQuery = horaQuery.relHoraActividad;
NSLog(#"Act --> %# y la categoria --> %# y la categoria --> %#", actividadQuery.titulo, actividadQuery.categoria, categoria);
}
}
If I do this query does not return good data that does not respect the category, I'm guessing, do not know why :S.
Try 2:
NSPredicate *predicateDia = [NSPredicate predicateWithFormat:#"relDiaHora.relHoraActividad.categoria == %#", categoria];
If I do the same query but only removing the "ANY" fails. Error: "reason: 'to-many key not allowed here'"
Your predicate
[NSPredicate predicateWithFormat:#"ANY relDiaHora.relHoraActividad.categoria == %#", categoria];
returns all days that have any hour with the given activity category. Your problem seems
to be that
NSSet *setHora = dia.relDiaHora;
returns all hours of the fetched day, not only the hours with the given activity category.
Therefore
HoraCD *horaQuery = [setHora anyObject];
is any hour of the fetched day, and need not have the given activity.
If you need the days and matching hours, you should execute a fetch request on the hours
instead:
NSEntityDescription *entityDescriptionHour = [NSEntityDescription entityForName:#"HourCD" inManagedObjectContext:managedObjectContext];
NSFetchRequest *requestHour = [[NSFetchRequest alloc] init];
NSPredicate *predicateHour = [NSPredicate predicateWithFormat:#"relHoraActividad.categoria == %#", categoria];
[requestHour setEntity:entityDescriptionHour];
[requestHour setPredicate:predicateHour];
NSError *errorHour;
NSArray *arrayHours = [managedObjectContext executeFetchRequest:requestHour error:&errorHour];
if ([arrayHours count] > 0) {
for (HourCD *hour in arrayHours) {
DiaCD *dia = hour.relHoraDia;
ActividadCD *actividad = hour.relHoraActividad;
// ...
}
}
OK, the first bit was wrong. However, still follow this note about naming.
A QUICK NOTE
You should not be calling your attributes "relItem1Item2". This is something that comes from relational databases. CoreData is not a relational database.
You should name them descriptively as to what they point to.
i.e.
relHoraActividad should be called actividad. As it is pointing to the actividad entity and it is a "to-one" relationship.
Also...
relActividadHora should be called horaCDs. It is pointing to the horaCD entity and it is a "to-many" relationship.
I think your setup is absurd. Why have complicated relationships to days and hours when you can simply use NSDate attributes?
From your image I see that you store other things in these entities that do not seem to have anything to do with hours or days.
To filter by day or time, you typically use a pattern like this
[NSPredicate predicateWithFormat:#"time > %# && time < %#", min, max];
I have the following core data relationship set up in my model.
Category -> Product -> CartProduct <<- Cart (See picture below).
But I have a hard time figuring out how to establish these relationships (in code). I have made 2 Objective-C Categories, with the names: CartProduct+Product & Cart+CartProduct.
CartProduct+Product contains the following code - this method is called, when the user pushes the "add to cart" button.
+ (CartProduct *)addProductToCartProducts:(Product *)theProduct inManagedObjectContext:(NSManagedObjectContext *)context {
CartProduct *cartProduct = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"CartProduct"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"products" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *cartProducts = [context executeFetchRequest:request error:&error];
if (!cartProducts || ([cartProducts count] > 1)) {
// handle error
} else if (![cartProducts count]) {
cartProduct = [NSEntityDescription insertNewObjectForEntityForName:#"CartProduct"
inManagedObjectContext:context];
/*This method is called from an background context, to prevent context conflicting, get nsmangedobject by its id, which is threadSafe. */
NSManagedObjectID *retID = [theProduct objectID];
//Setup One-One relationship from Product to CartProduct
cartProduct.product = (Product *) [context objectWithID:retID];
/*Call method from class Cart+CartProduct to establish to-many relationship from Cart to CartProduct.*/
[Cart addCartProductToCart:cartProduct inManagedObjectContext:context];
} else {
cartProduct = [cartProducts lastObject];
}
return cartProduct;
}
Cart+CartProduct contains the following code:
+ (Cart *)addCartProductToCart:(CartProduct *)theCartProduct inManagedObjectContext:(NSManagedObjectContext *)context {
Cart *cart = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"CartProduct"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"productsInCart" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *cartProducts = [context executeFetchRequest:request error:&error];
if (!cartProducts || ([cartProducts count] > 1)) {
// handle error
} else if (![cartProducts count]) {
cart = [NSEntityDescription insertNewObjectForEntityForName:#"Cart"
inManagedObjectContext:context];
[cart addProductsInCartObject:theCartProduct];
} else {
cart = [cartProducts lastObject];
}
return cart;
}
Now I want to view, the objects the user has added to his Cart, I therefore fetch from the Cart entity. But I can't figure out if the I have "connected" the relationships correct? and how to fetch the products in the cart, so I can show the products. (which is in a one-many relationship with CartProduct).
So my question is:
Is the relationship established correct?
How do I manage to fetch the products which is added to the cart?
NB: Earlier this year, I made the following post: Add to cart functionality - Core data and this question is based on that.
Your relationships seem mostly correct, though I think you do not even need the CartProduct entity. Core Data can handle many-to-many relationships behind the scenes. You can just have Cart have a relationship "products" and Product have the inverse relationship "inCarts". A Cart can have many Products and a Product can be in many Carts.
But anyway, you can keep it as is. The only problem is that Product's relationship "cartProduct" should be to-many (and therefore should be named "cartProducts". That is, if you want it to be possible that a product is in more than one cart. Also, be sure you have all your inverse relationships correctly defined.
Your code looks all wrong to me. It's way too complicated for achieving what you're trying to do. Core Data makes it simple. It looks like you are trying to create a new Cart and add the Product to it. You shouldn't need to do any fetching to do this. It seems like maybe you are thinking like a database. With Core Data, you do not think about modifying tables, you think about setting pointers between objects. I would do it this way:
+ (Cart *)startNewCartWithProduct:(Product *)product inManagedObjectContext:(NSManagedObjectContext *)context {
Cart *cart = [NSEntityDescription insertNewObjectForEntityForName:#"Cart" inManagedObjectContext:context];
CartProduct *cartProduct = [NSEntityDescription insertNewObjectForEntityForName:#"CartProduct" inManagedObjectContext:context];
// with correct inverse relationships, automatically adds cartProduct to cart:
cartProduct.cart = cart;
cartProduct.product = product;
return cart;
}
Now for your question #2. To get the products in a cart, use code like this:
+ (NSSet *)productsInCart:(Cart *)cart {
NSMutableSet *result = [NSMutableSet setWithCapacity:[cart.productsInCart size]];
for (CartProduct *cartProduct in cart.productsInCart)
[result addObject:cartProduct.product];
return result;
}
Ha det bra!
I have two entities. (Deal, Customer)
Deal and Customer have 1:1 relationship. so Deal has customer, and Customer has deal.
first, I made Customer object named "John".
second, I made Deal object and set customer with "John" (#1 deal)
third, I made another Deal object and set customer with "John" (#2 deal)
at that time, I found some problem.
that is #1 deal's customer set nil automatically, and #2 deal's customer is "John".
how can I solve that?
ps1. I got the data from web server as JSON like this
deals = [id: .., ..., customer: { ... }]
ps2. I update objects whenever receive data from server.
+ (Deal *)dealWithDealsDictionary:(NSDictionary *)dic inManagedObjectContext:(NSManagedObjectContext *)context
{
Deal *deal = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Deal"];
request.predicate = [NSPredicate predicateWithFormat:#"deal_id = %#", [dic[#"id"] description]];
// Execute the fetch
NSError *error = nil;
NSArray *matches = [context executeFetchRequest:request error:&error];
// Check what happened in the fetch
if (!matches || ([matches count] > 1)) { // nil means fetch failed; more than one impossible (unique!)
deal = [matches lastObject];
// handle error
} else if (![matches count]) {
deal = [NSEntityDescription insertNewObjectForEntityForName:#"Deal" inManagedObjectContext:context];
} else {
deal = [matches lastObject];
}
deal.deal_id = [dic[#"id"] description];
deal.deal_status = [dic[#"deal_status"] description];
deal.deal_stage = [dic[#"deal_stage"] description];
deal.deal_desc = [dic[#"deal_desc"] description];
deal.localized_deal_status = [dic[#"localized_deal_status"] description];
deal.localized_deal_stage = [dic[#"localized_deal_stage"] description];
if (dic[#"customer"]) {
[context performBlock:^{
deal.customer = [Customer customerWithDictionary:dic[#"customer"] inManagedObjectContext:context];
}];
}
return deal;
}
you don't have a 1:1 relationship:it is 1:N
2 deals have the same customer, so 1 customer has N deals.
CoreData wanted to keep the 1:1 constraints where 1 deal has always 1 unique customer and vice versa.
Change to one-to-many
Make the relationship 1 to many or many to many if you want a customer to have many deals and / or many customers each to have many deals (where customers can each have the same deal).
The reference was set to nil because you said there could only be 1 reference at a time.
I'm dong data processing in a child moc in a background queue. I need to query the database by ID so that I can differentiate updating-existing-object from creating-new-object. I found most of the time(the total processing time is about 2s for 50 items) is consumed by executeFetchRequest:error:. The NSPredicate is of the simplest form — only to match a single ID attribute(ID attribute is already indexed), and the NSFetchRequest should return one or none(ID is unique). Is there any way to optimize this kind of NSFetchRequest?
Here is my current code:
+ (User *)userWithID:(NSNumber *)ID inManagedObjectContext:(NSManagedObjectContext *)context {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"User"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ID == %#", ID];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:1];
NSError *error = nil;
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
abort();
}
if ([users count] == 1) {
return [users objectAtIndex:0];
} else if ([users count] > 1) {
// Sanity check.
…
} else {
return nil;
}
}
As #ChrisH pointed out in comments under the question, doing a fetch for every ID is no good. So I changed my processing flow to this:
Enumerate data the first time to extract IDs.
Do a single fetch to fetch all existing users matching IDs and put them in a dictionary keyed by ID(named as existingUsers).
Enumerate data the second time to do the real processing: in each iteration, either update one existing user found in existingUsers or create a new user, add it into existingUsers if it is new.
The code is almost doubled, but so is the performance. Really good tradeoff!
To expand on my comment to the original question, it's not efficient to repeatedly perform fetch requests with Core Data when importing data.
The simplest approach, as #an0 indicated, is to perform one fetch of all the existing objects you will be checking against, and then constructing an NSDictionary containing the objects with the attribute you will be checking as keys. So sticking with the original User and userID example:
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"User"];
NSError *error = nil;
NSArray *users = [context executeFetchRequest:fetchRequest error:&error];
if (error) {
//handle appropriately
}
NSMutableDictionary *userToIdMap = [NSMutableDictionary dictionary];
for (User *user in users){
[userToIdMap setObject:user forKey:user.ID];
}
Now in your method that processes new data you can check the userToIdMap dictionary instead of making fetch requests.
A more sophisticated approach, suited to larger data sets, is outlined in the Core Data Programming Guide's Efficently Importing Data. Take a look at the section called 'Implementing Find-Or-Create Efficiently'. The approach suggested by Apple here misses out some code regarding how to walk the arrays you create, and my solution to that problem is in this SO question: Basic array comparison algorithm