Comparing NSArray values - ios

So i have 2 arrays, one i have from my local sqlite db and the other i pull down from a server.
When i try to use isEqualToArray: i'll get a NO even though they are similar. This NSHipster article http://nshipster.com/equality/ told me that it's because they compare identity in the memory..? So i'll need some way of only comparing the values?
All help will be appreciated, i've been staring myself blind on this one for quite some time.
So i have 2 arrays:
NSArray *arr1 = [[NSArray alloc] initWithObjects:#"1",#"Hello",#"member",#"Janus", nil];
NSArray *arr2 = [[NSArray alloc] initWithObjects:#"1",#"Hello",#"member",#"Janus", nil];
When i print them they look exactly the same, when i run NSSet on them like Pablo A suggest i don't get a match and either when i run isEqualToArray on the arrays directly. They are in order and they are always the same number, they are identically but the code won't recognize it.

Sorry, I edit my answer as I realised it may fail. Here you have a better solution:
NSSet *set1 = [NSSet setWithArray:arr1];
NSSet *set2 = [NSSet setWithArray:arr2];
if ([set1 isEqualToSet:set2]) {
// equal
NSLog(#"They contain same objects");
if(arr1.count == arr2.count) {
NSLog(#"They are exactly the same (not checking order)");
}
}
Edited according to comments.

Related

Can't have access to NSDictionary in NSArray

I have a very strange behaviour with NSArray.
Firstly, i parsed an XML file and return a valid NSArray with NSDictionary inside:
NSArray *arrayOfDict = [parser parseWithXMLFile:filename];
In debugger it's fully valid. Then, i want to proccess all dictionaries in this array:
NSMutableArray* arrayOfProducts = [[NSMutableArray alloc] init];
for (NSDictionary* dict in arrayOfDict) {
Product* product = [[Product alloc] initWithName:dict[#"name"]
type:dict[#"type"]
imageName:dict[#"image"]
description:dict[#"description"]];
[arrayOfProducts addObject:product];
[product release];
}
And in this loop is a problem: a dict variable has value nil. And i don't know what to do with this. In debugger i evaluate value of arrayOfDict[someIndex] and get a right value, but in the programm itself it doesn't work.
May be it's the problem with MRR, i don't feel myself confidenly while using MRR and there is a mistake of using it.
P.S. I know that using MRR is stupid today, but in this project i must use it.

Compare objects in two Collections

I have the following scenario:
I have two collection classes (could be NSArray, NSMutableArray, NSSet, NSOrderedSet or whatever would be best suited for this case), which hold unique objects of the same type (unique in the sense that for all objects in the collections for no two elements the isEqual method would return true).
Lets say the first collection instance holds the following objects (1,2,3,4,5) and the second one (2,3,4,6,7). Now I need a method that returns the difference between the two collections, with the extra info what exactly the difference from each collection was.
An example result for the example would be: (1,5) was removed from the first collection and (6,7) added two the second collection.
I know if I use the NSMutableArray with a sorted list and decide which list has more elements than the other, I could use removeObjectsInArray to get a list of the different objects (like described in Compare two arrays with the same value but with a different order or in How to compare and remove common objects( NSDictionaries) from 2 NSMutableArray?), but don't really know which objects was in which collection. I could create a temporary collection and put the result of removeObjectsInArray in that array and compare the other two initial arrays with the temporary array. Seems little verbose though. Is there a better way that I don't know of?
I found a much slicker way for you to do what you want by using NSPredicate. When I run the following code:
NSArray *firstArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7", nil];
NSArray *secondArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"8",nil];
NSArray *itemsMissingFromSecondArray = [firstArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT SELF IN %#", secondArray]];
NSArray *itemsMissingFromFirstArray = [secondArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"NOT SELF IN %#", firstArray]];
NSLog(#"itemsMissingFromFirstArray=%#\nitemsMissingFromSecondArray=%#", itemsMissingFromFirstArray, itemsMissingFromSecondArray);
I get the following output showing what was missing from each array that was in the other array:
itemsMissingFromFirstArray=(
8
)
itemsMissingFromSecondArray=(
4,
5,
6,
7
)
Less code than sorting and merging, doesn't use a bunch of temporary arrays, and simple enough to read.
NOTE: If someone also wants to know the items that are in both arrays, the solution is similarly simple:
NSArray *itemsFoundInBothArrays = [firstArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF IN %#", secondArray]];
I think if you want to know the difference, you can make use of NSMutableSet and the minusSet function.
[set1 minusSet:set2];
will give you the elements in set1 but not in set2 straight away. So you don't need any temp collection and compare with original collection again.
Otherwise, if you only want to remove the elements, you can make use of NSArray and do sth like:
[secondArray removeObjectsInArray:firstArray];
Edited:
To find all the diff in one shot:
[ [set1 unionSet:set2] minusSet: [set1 intersectSet:set2] ];
+ (NSArray *) removeObjectsFromArray :(NSArray *)arrayToRemoveFrom thatAreAlsoIn:(NSArray *)arrayOfItemsToRemove
{
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:arrayToRemoveFrom];
[newArray removeObjectsInArray:arrayOfItemsToRemove];
return newArray;
}
+(void) findArrayDifferences
{
NSArray *bigArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"7", nil];
NSArray *smallArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"8",nil];
NSArray *itemsInBigArrayThatAreNotInSmallArray = [self removeObjectsFromArray:bigArray thatAreAlsoIn:smallArray];
NSArray *itemsThatAreInBothArrays = [self removeObjectsFromArray:bigArray thatAreAlsoIn:itemsInBigArrayThatAreNotInSmallArray];
NSArray *itemsInSmallArrayThatAreNotInBigArray = [self removeObjectsFromArray:smallArray thatAreAlsoIn:itemsThatAreInBothArrays];
NSLog(#"itemsInBigArrayThatAreNotInSmallArray=%#\nitemsThatAreInBothArrays=%#\nitemsInSmallArrayThatAreNotInBigArray=%#", itemsInBigArrayThatAreNotInSmallArray, itemsThatAreInBothArrays, itemsInSmallArrayThatAreNotInBigArray);
}
This results in the following output:
itemsInBigArrayThatAreNotInSmallArray=(
4,
5,
6,
7
)
itemsThatAreInBothArrays=(
1,
2,
3
)
itemsInSmallArrayThatAreNotInBigArray=(
8
)

How do i get unique contents from my NSMutableArray?

I have a UITableView and am displaying contents from my NSMutableArray. Following is array format
(
{
Name = "ANS";
VersionNo = 6;
},
{
Name = "O-Hydro";
Version = 6;
},
{
Name = "ANS";
Version = 6;
},
{
Name = "ANTIChorosAnticholinergic";
Version = 6;
}
)
From this I need to display only unique "Name" (like in this I can see 2 "ANS" I need only one).
How can I do this in iOS?
I tried following but its not working
uniqueArray= [[NSMutableSet setWithArray: groupDetails] allObjects];
but in this way I can do only for NSArray not NSMutableArray.
Pls help me
You can use following line of code to convert your NSArray to NSMutableArray,
NSArray *uniqueArray= [[NSMutableSet setWithArray:groupDetails] allObjects];
NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithArray:uniqueArray];
You could simply add mutableCopy.
But wait, before you do it. Arrays and sets have two differences:
Arrays can contain duplicates, sets cannot.
Arrays are ordered, sets are not.
So doing what you are doing, you lose the duplicates (intentionally), but the order, too (probably not intentionally).
I do not know, whether this is important for you, but for other readers it might be. So it is the better approach to do that with NSOrderedSet instead of NSSet:
NSOrderedSet *uniqueList = [NSOrderedSet orderedSetWithArray:array];
In many cases an ordered set is exactly what you want. (Probably it has been from the very beginning and the usage of NSArray was wrong. But sometimes you get an array.) If you really want an array at the end of the day, you can reconvert it:
array = [uniqueList.array mutableCopy];
If you just want an array of unique name values, you can use #distinctUnionOfObjects with valueForKeyPath -
NSArray *uniqueArray=[groupDetails valueForKeyPath:#"#distinctUnionOfObjects.name"];
But if you want the array to contain the dictionaries that correspond to the unique names then you need to do a little more work -
NSMutableArray *uniqueArray=[NSMutableArray new];
NSMutableSet *nameSet=[NSMutableSet new];
for (NSDictionary *dict in groupDetails) {
NSString *name=dict[#"name"];
if (![nameSet containsObject:name]) {
[uniqueArray addObject:dict];
[nameSet addObject:name];
}
}

Sorting the Key/Values stored in NSArray

I have a
NSDictionary* dict = [[NSDictionary alloc]initWithObjectsAndKeys::arrayOne,#"Plants",arrayTwo,#"Animals"),arrayThree,#"Birds",nil];`
self.displayArray =[[dict allKeys] sortedArrayUsingSelector:#selector(compare:)];
Everything works fine, I am able to see all the key value/pair in the table but they are in sorted order. i.e Animals,Birds,Plants.
But I want to display as Plants,Animals,Birds.
Can anyone tell me how to sort the array in my customized order?
I have googled and found that we can use NSSortDescriptor for sorting. But I am not very clear with that. Can anyone help me ?
As your ordering doesnt follow any natural order, a simple solution could be to keep track of the order with another array
NSArray *array1 = [NSArray arrayWithObjects:#"rose",#"orchid",#"sunflower",nil];
NSArray *array2 = [NSArray arrayWithObjects:#"dog", #"cat",#"ogre",#"wookie", nil];
NSArray *array3 = [NSArray arrayWithObjects:#"parrot",#"canary bird",#"tweety",#"bibo",nil];
NSArray *keys = [NSArray arrayWithObjects:#"Plants",#"Animals",#"Birds", nil];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
array1,[keys objectAtIndex:0],
array2,[keys objectAtIndex:1],
array3,[keys objectAtIndex:2],
nil];
for (NSString *key in keys) {
NSLog(#"%# %#", key, [dict objectForKey:key]);
}
Matt shows in his fantastic blog, how to create a ordered dictionary, that essentially uses another array to keep the order just as I showed here: OrderedDictionary: Subclassing a Cocoa class cluster
You're on the right track, Cyril.
Here is some Apple documentation on "Creating and using Sort Descriptors"
Basically you need to subclass NSSortDescriptor and in your subclass, implement your own "compare:" method (you can actually name it anything you want; it needs to return a "NSComparisonResult") that somehow logically returns "Plants" before "Animals".

What's the standard convention for creating a new NSArray from an existing NSArray?

Let's say I have an NSArray of NSDictionaries that is 10 elements long. I want to create a second NSArray with the values for a single key on each dictionary. The best way I can figure to do this is:
NSMutableArray *nameArray = [[NSMutableArray alloc] initWithCapacity:[array count]];
for (NSDictionary *p in array) {
[nameArray addObject:[p objectForKey:#"name"]];
}
self.my_new_array = array;
[array release];
[nameArray release];
}
But in theory, I should be able to get away with not using a mutable array and using a counter in conjunction with [nameArray addObjectAtIndex:count], because the new list should be exactly as long as the old list. Please note that I am NOT trying to filter for a subset of the original array, but make a new array with exactly the same number of elements, just with values dredged up from the some arbitrary attribute of each element in the array.
In python one could solve this problem like this:
new_list = [p['name'] for p in old_list]
or if you were a masochist, like this:
new_list = map(lambda p: p['name'], old_list)
Having to be slightly more explicit in objective-c makes me wonder if there is an accepted common way of handling these situations.
In this particular case Cocoa is not outdone in succinctness :)
NSArray *newArray = [array valueForKey:#"name"];
From the NSArray documentation:
valueForKey:
Returns an array containing the
results of invoking valueForKey: using
key on each of the receiver's objects.

Resources