I'm quite new to StackOverflow, so apologies in advance if I forgot some critical element in my question. It's a long one, so bear with me!
Iterating over JSON-webservice result:
For an IOS5 iPad app, I collect JSON information from a webservice and via:
NSData *data = [NSData dataWithContentsOfURL:url];
NSDictionary *json =
[NSJSONSerialization JSONObjectWithData:data options:kNilOptions
error:&error];
I'll iterate over the returned NSDictionary
and store the returned objects and their properties as a custom class.
The custom class contains several properties, including an AmountInShoppingCart
-value.
I use this custom class to represent items that we sell in our store, so an NSMutableArray
is made of all the returned objects and shown to the user.
If the AmountInShoppingCart value > 0
, then the object will be put in the ShoppingCart
NSMutableArray
(which is a singleton).
Checking for duplicates in ShoppingCart:
To make sure the ShoppingCart
doesn't contain any duplicate entries, I've got a function like this in the ShoppingCartController
(also borrowed from SO):
- (void)checkForDuplicates
{
NSMutableSet *seen = [NSMutableSet set];
NSUInteger i = 0;
NSLog(@"ShoppingCartArray count: %u",[ShoppingCartArray count]);
//Iterate over the Array to check for duplicates, and to sync the list with the
cart.
while (i < [ShoppingCartArray count]) {
id obj = [ShoppingCartArray objectAtIndex:i];
if ([seen containsObject:obj]) {
if ([obj isKindOfClass:[MyCust class]]){
MyCust *cust = [ShoppingCartArray objectAtIndex:i];
MyCust *seenCust = [seen member:obj];
seenCust.AmountInShoppingCart = cust.AmountInShoppingCart;
[ShoppingCartArray removeObjectAtIndex:i];
}} else {seen addObject:obj];
i++;}}}
Equality & Hashing
To accomplish this, I've overridden the isEqual:
-function of the MyCust
-class so that it'll look at the externalItemID
-property, which is the only property that is definitely unique for each class object.
I picked this function up from StackOverflow:
- (BOOL)isEqual:(id)otherObject;
{
if ([otherObject isKindOfClass:[MyCust class]]) {
MyCust *otherCust= (MyCust *)otherObject;
if (externalItemId != [otherCust externalItemId]) return NO;
return YES;
}
return NO;
}
- (NSUInteger) hash;
{
return [[self externalItemId] hash];
}
Sync the two arrays:
It can happen that people request the same result from the JSON-webservice, since it uses the MyCust's size, height and diameter to return a set of objects that adhere to those sizes.
So I use the following function to check and 'sync' the two NSMutableArrays with the new results:
In: RESULTLISTCONTROLLER.M
- (void) syncWithCart
{
ShoppingCart *sc = [ShoppingCart sharedCart];
[sc checkForDuplicates];
NSMutableSet *listSet = [NSMutableSet setWithArray:resultList];
NSUInteger i = 0;
while (i < [sc.ShoppingCartArray count]) {
id ShopObject = [sc.ShoppingCartArray objectAtIndex:i];
if ([listSet containsObject:ShopObject])
{
MyCust *listCust = [listSet member:ShopObject];
MyCust *shopCust = ShopObject;
listCust.AmountInShoppingCart = shopCust.AmountInShoppingCart;
[listSet removeObject:ShopObject];
} else {i++;}}}
This all works pretty well until...
The 2nd webservice!
Now here comes the good part: There's a 2nd JSON-WebService the app uses to retrieve external supplier stock. It sometimes contains the same MyCust
objects, but contains extra information that has to be parsed into the existing objects.
I basically get the first WebService's result into the getMyCustArray
and the other in the getExternalCustArray
. I then check if getMyCustArray
contains an object in the getExternalCustArray
and update the additional information on the objects and delete the similar object in getExternalCustArray
. Any leftover objects only available from the supplier will be added using:
mergedCustArray = [NSMutableArray arrayWithArray:[getMyCustArray arrayByAddingObjectsFromArray:getExternalCustArray]];
Now the problem is, that when the user checks the same sizes twice, the App doesn't seem to consider the new results as the same.
When I do an NSLog of the ShoppingCartArray, the following shows: (original item names obfuscated)
First Search:
MyCust: SideShowBobsEarWarmers
Hash: **2082010619**
AmountInShoppingCart: 4
**<MyCust: 0x81cee50>**",
"
MyCust: SolidSnakesCardBoardBox
Hash: **4174540990**
AmountInShoppingCart: 4
**<MyCust: 0x8190d10>**"
Second Search: the ResultList doesn't retain the MyCust's AmountInShoppingCart values, so I add them to the ShoppingCart again from the ResultList.
MyCust: SideShowBobsEarWarmers
Hash: **2082010619**
AmountInShoppingCart: 4
**<MyCust: 0x81cee50>**",
MyCust: SolidSnakesCardBoardBox
Hash: **4174540990**
AmountInShoppingCart: 4
**<MyCust: 0x8190d10>**"
"
// Different Pointers, although they should be considered Equal.
MyCust: SideShowBobsEarWarmers
Hash: **2082010619**
AantalInWinkelWagen: 3
**<MyCust: 0x74bc570>**",
"
MyCust: SolidSnakesCardBoardBox
Hash: **4174540990**
AantalInWinkelWagen: 2
**<MyCust: 0x74bc7b0>**"
So somehow the compiler allocates those objects in a different memory address/pointer, and then breaks the isEqual:
function, as far as I can tell.
My guts tell me there's something to do with NSCopy(able)
, but I'm not quite sure how I'd implement that. If anyone got some good tips for a Shopping Cart system, that's always welcome too. I've got half a mind converting all this to Core Data but would like to have the core functionality in place before doing that.
In isEqual: method, the line "if (externalItemId != [otherCust externalItemId]) return NO;" is wrong. I think it should be if (![externalItemId:isEqual:[otherCust externalItemId]]) return NO;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With