Objective-C - How to compare arrays and extract the difference? - objective-c

Possible duplicate: comparing-two-arrays
I have two NSArray and I'd like to create a new Array with objects from the second array but not
included in the first array.
Example:
NSMutableArray *firstArray = [NSMutableArray arrayWithObjects:#"Bill", #"Ben", #"Chris", #"Melissa", nil];
NSMutableArray *secondArray = [NSMutableArray arrayWithObjects:#"Bill", #"Paul", nil];
The resulting array should be:
[#"Paul", nil];
I solved this problem with a double loop comparing objects into the inner one.
Is there a better solutions ?

[secondArray removeObjectsInArray:firstArray];
This idea was taken from another answer.

If duplicate items are not significant in the arrays, you can use the minusSet: operation of NSMutableSet:
NSMutableArray *firstArray = [NSMutableArray arrayWithObjects:#"Bill", #"Ben", #"Chris", #"Melissa", nil];
NSMutableArray *secondArray = [NSMutableArray arrayWithObjects:#"Bill", #"Paul", nil];
NSSet *firstSet = [NSSet setWithArray:firstArray];
NSMutableSet *secondSet = [NSMutableSet setWithCapacity:[secondArray count]];
[secondSet addObjectsFromArray:secondArray];
[secondSet minusSet:firstSet]; // result is in `secondSet`

I want to compare images from two NSArray.
One Array, I was getting from Core Database. Second I have constant array objects.
I want to know that object of second array is present in Core database or not.
Here is code which i used.
// All object from core data and take into array.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]initWithEntityName:#"student"];
NSArray *dbresult = [[NSArray alloc]init];
NSError *error;
#try {
dbresult = [context executeFetchRequest:fetchRequest error:&error];
}
#catch (NSException *exception) {
NSString *logerror = [NSString stringWithFormat:#"error in fetching Rooms from coredata = %#",exception.description];
NSLog(logerror)
}
#finally {
}
/*
Get Unused images from list
*/
NSMutableArray *usedImages = [dbresult valueForKey:#"roomImageLocalPath"];
NSMutableSet *fSet = [NSMutableSet setWithArray:usedImages];
NSMutableSet *sSet = [NSMutableSet setWithCapacity:[newImages count]];
[sSet addObjectsFromArray:newImages];
[sSet minusSet:fSet];
NSArray *unusedImages = [secondSet allObjects];
NSLog(#"unusedImages %#",unusedImages);

Related

Sorting JSON Data by date

Hi I'm getting data online from a JSON file and I'm trying to sort it by date in descending order, I've done it before using an XML parser using an RSS feed and tried to use the same concept but can't seem to get it and it crashes every time.
NSData *data = [NSData dataWithContentsOfURL:url];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSDictionary *dataDict = [dict objectForKey:#"data"];
NSArray *array = [dataDict objectForKey:#"items"];
for (int i=0; i<[array count]; i++) {
SongInfo *song = [[SongInfo alloc]init];
NSMutableDictionary *entry = [array objectAtIndex:i];
song.uploaded = [entry objectForKey:#"uploaded"];
song.uploader = [entry objectForKey:#"uploader"];
NSComparator comparator = ^(NSDictionary *a, NSDictionary *b) {
return [a[#"uploaded"] compare:b[#"uploaded"]];
};
NSUInteger index = [songsArray indexOfObject:entry
inSortedRange:NSMakeRange(0, [songsArray count])
options:NSBinarySearchingInsertionIndex
usingComparator:comparator];
[songsArray insertObject:song atIndex:index];
EDIT: Managed to fix it by using the NSSortDescriptor and putting it into an array and then back into the same array, not sure if there is a better way to do this but this is how I did it...
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"uploaded"
ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [songsArray sortedArrayUsingDescriptors:sortDescriptors];
songsArray = [NSMutableArray arrayWithArray:sortedArray];
// [songsArray addObject:[song copy] atIndex:index];
[songsArray addObject:song];
You're comparing objects inserted in the songsArray with a comparator that takes in two dictionaries. It seems like the comparator should compare SongInfo objects rather than NSDictionaries.
We can't see what songsArray is. Creating a sorted array manually like you do is really inefficient. Call the NSArray method sortedArrayUsingComparator instead, and there is no need to create SongInfo objects.
On the other hand, if an array of SongInfo* is what you want, create that array first as an NSMutableArray with all the SongInfo's from the JSON data, then sort that array by calling sortUsingComparator.
Note that in the comparator block you can actually use the type of the object: So in the first case NSDictionary* instead of id, in the second case SongInfo* instead of id.

Check that the contents of one NSArray are all in another array

I have one NSArray with names in string objects like this:#[#"john", #"smith", #"alex",
#"louis"], and I have another array that contains lots of names. How can I check that all the objects in the first array are in the second?
NSSet has the functionality that you are looking for.
If we disregard performance issues for a moment, then the following snippet will do what you need in a single line of code:
BOOL isSubset = [[NSSet setWithArray: array1] isSubsetOfSet: [NSSet setWithArray: mainArray]];
Use this code..
NSArray *temp1 = [NSArray arrayWithObjects:#"john",#"smith",#"alex",#"loui,#"Jac", nil];
NSArray *temp2 = [NSArray arrayWithObjects:#"john",#"smith",#"alex",#"loui,#"Rob", nil];
NSMutableSet *telephoneSet = [[NSMutableSet alloc] initWithArray:temp1] ;
NSMutableSet *telephoneSet2 = [[NSMutableSet alloc] initWithArray:temp2];
[telephoneSet intersectSet:telephoneSet2];
NSArray *outPut = [telephoneSet allObjects];
NSLog(#"%#",outPut);
output array contains:
"john","smith","alex","loui
as per your requirement.
Run a loop and use isEqualToStiring to verify whether array1 objects exists in mainArray.
int num_of_matches = 0;
for(NSString *name in mainArray)
{
if(array1 containsObject:name){
num_of_matches++;
}
}
if(num_of_matches == [array1 count]{
// All objects present
}else {
// Matched number is equal of number_of_matches
}
If you just need to check if all objects from array1 are in mainArray, you should just use NSSet
e.g.
BOOL isSubset = [[NSSet setWithArray:array1] isSubsetOfSet:[NSSet setWithArray:mainArray]]
if you need to check which objects are in mainArray, you should take a look at NSMutableSet
NSMutableSet *array1Set = [NSMutableSet setWithArray:array1];
[array1Set intersectSet:[NSSet setWithArray:mainArray]];
//Now array1Set contains only objects which are present in mainArray too
Use NSArray filteredArrayUsingPredicate: method. Its really fast to find out similar types of object in both arrays
NSPredicate *intersectPredicate = [NSPredicate predicateWithFormat:#"SELF IN %#", otherArray];
NSArray *intersectArray = [firstArray filteredArrayUsingPredicate:intersectPredicate];
From above code intersect array gives you same objects which are in other array.
Try this way;
NSArray *mainArray=#[#"A",#"B",#"C",#"D"];
NSArray *myArray=#[#"C",#"x"];
BOOL result=YES;
for(id object in myArray){
if (![mainArray containsObject:object]) {
result=NO;
break;
}
}
NSLog(#"%d",result); //1 means contains, 0 means not contains
You can use the concept of [NSArray containsObject:], where your objects will be from your array1 like you say "john","smith","alex","loui"
NSArray *array1 = [NSArray arrayWithObjects:#"a", #"u", #"b", #"v", #"c", #"f", nil];
NSMutableArray *mainArray = [NSMutableArray arrayWithObjects:#"a", #"u", #"I", #"G", #"O", #"W",#"Z",#"C",#"T", nil];
int j=0;
for(int i=0; i < mainArray.count; i++)
{
if (j < array1.count)
{
for( j=0; j <= i; j++)
{
if([[mainArray objectAtIndex:i] isEqualToString:[array1 objectAtIndex:j]] )
{
NSLog(#"%#",[mainArray objectAtIndex:i]);
}
}
}
}

Sort NSMutableArray based on strings from another NSArray

I have an NSArray of strings that I want to use as my sort order:
NSArray *permissionTypes = [NSArray arrayWithObjects:#"Read", #"Write", #"Admin", nil];
I then have a NSMutableArray that may or may not have all three of those permissions types, but sometimes it will only be 2, sometimes 1, but I still want it sorted based on my permissionsTypes array.
NSMutableArray *order = [NSMutableArray arrayWithArray:[permissions allKeys]];
How can I always sort my order array correctly based on my using the permissionTypes array as a key?
I would go about this by creating a struct or an object to hold the permission types.
Then you can have...
PermissionType
--------------
Name: Read
Order: 1
PermissionType
--------------
Name: Write
Order: 2
and so on.
Then you only need the actual array of these objects and you can sort by the order value.
[array sortUsingComparator:^NSComparisonResult(PermissionType *obj1, PermissionType *obj2) {
return [obj1.order compare:obj2.order];
}];
This will order the array by the order field.
NSMutableArray *sortDescriptors = [NSMutableArray array];
for (NSString *type in permissionTypes) {
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:type ascending:YES] autorelease];
[sortDescriptors addObject:descriptor];
}
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
Use whichever sorting method on NSMutableArray you prefer, you will either provide a block or a selector to use for comparing two elements. In that block/selector rather than comparing the two strings passed in directly look each up in your permissionTypes array using indexOfObject: and compare the resulting index values returned.
I suggest you another approuch:
- (void)viewDidLoad
{
[super viewDidLoad];
arrayPermissions = [[NSMutableArray alloc] init];
NSDictionary *dicRead = [NSDictionary dictionaryWithObjectsAndKeys:
#"Read", #"Permission", nil];
NSDictionary *dicWrite = [NSDictionary dictionaryWithObjectsAndKeys:
#"Write", #"Permission", nil];
NSDictionary *dicAdmin = [NSDictionary dictionaryWithObjectsAndKeys:
#"Admin", #"Permission", nil];
NSLog(#"my dicRead = %#", dicRead);
NSLog(#"my dicWrite = %#", dicWrite);
NSLog(#"my dicAdmin = %#", dicAdmin);
[arrayPermissions addObject:dicRead];
[arrayPermissions addObject:dicWrite];
[arrayPermissions addObject:dicAdmin];
NSLog(#"arrayPermissions is: %#", arrayPermissions);
// create a temporary Dict again
NSDictionary *temp =[[NSDictionary alloc]
initWithObjectsAndKeys: arrayPermissions, #"Permission", nil];
// declare one dictionary in header class for global use and called "filteredDict"
self.filteredDict = temp;
self.sortedKeys =[[self.filteredDict allKeys]
sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sortedKeys is: %i", sortedKeys.count);
NSLog(#"sortedKeys is: %#", sortedKeys);
}
hope help

Sort NSArray of custom objects based on sorting of another NSArray of strings

I have two NSArray objects that I would like to be sorted the same. One contains NSString objects, the other custom Attribute objects. Here is what my "key" NSArray looks like:
// The master order
NSArray *stringOrder = [NSArray arrayWithObjects:#"12", #"10", #"2", nil];
The NSArray with custom objects:
// The array of custom Attribute objects that I want sorted by the stringOrder array
NSMutableArray *items = [[NSMutableArray alloc] init];
Attribute *attribute = nil;
attribute = [[Attribute alloc] init];
attribute.assetID = #"10";
[items addObject:attribute];
attribute = [[Attribute alloc] init];
attribute.assetID = #"12";
[items addObject:attribute];
attribute = [[Attribute alloc] init];
attribute.assetID = #"2";
[items addObject:attribute];
So, what I would like to do is use the stringOrder array to determine the sorting of the items array of custom objects.
How can I do this?
Hereby, I compare directly the index of obj1.assetID in stringOrder with the index of obj2.assetID in stringOrder (using Objective-C literals for #() to transform NSString => NSNumber)
[items sortUsingComparator:^NSComparisonResult(Attribute *obj1, Attribute *obj2) {
return [#([stringOrder indexOfObject:obj1.assetID]) compare:#([stringOrder indexOfObject:obj2.assetID])]
}];
Or without ObjC literals :
[items sortUsingComparator:^NSComparisonResult(Attribute *obj1, Attribute *obj2) {
return [[NSNumber numberWithInt:[stringOrder indexOfObject:obj1.assetID]] compare:[NSNumber numberWithInt:[stringOrder indexOfObject:obj2.assetID]]]
}];
While cwehrungs answer will get the job done, the performance is not great on relatively small arrays.
Here is another method for performing the same kind of sort that is a bit quicker (though still far from perfect):
NSMutableArray *sorted = [NSMutableArray array];
// pre-populate with objects
for (int i = 0; i < stringOrder.count; i++)
{
[sorted addObject:[NSNull null]];
}
// place the items at the correct position
for (Attribute *a in items)
{
NSUInteger idx = [stringOrder indexOfObject:a.assetID];
if (idx != NSNotFound)
{
[sorted setObject:a atIndexedSubscript:idx];
}
}
// finally remove all the unecesarry placeholders if one array was smaller
[sorted removeObject:[NSNull null]];
Comparison
Here are the results form running the two methods on an iPhone 5:
sortUsingComparator:
100 - 0.012 s
1000 - 1.116 s
2000 - 4.405 s
3000 - 9.028 s
prepopulated array
100 - 0.003 s
1000 - 0.236 s
2000 - 0.917 s
3000 - 2.063 s
There are a couple approaches you could take.
You could store your Attribute objects in an NSDictionary, with the keys being the strings in your stringOrder array. Then, you could get a sorted array of the keys and use that to populate whatever view you're using to display them:
NSArray* sortedKeys = [dict keysSortedByValueUsingComparator:^(id obj1, id obj2) {
return [obj1 compareTo:obj2];
}
The other is that you make the sort order an intrinsic property of your Attribute object, so an array of Attributes can be sorted directly. I would only recommend taking this approach if the sort order is actually an intrinsic property of your Attributes object. If it isn't and you do this, you'll wind up storing presentation information where it doesn't belong.
Here's an example:
NSArray* sortedAttrs = [attributes sortedArrayUsingComparator:^(id obj1, id obj2) {
// Perform comparison of Attribute's, ahem, attributes
}
Here is the solution that I came up with that works extremely well. Anyone see performance issues with this?
for (Attribute *a in items) {
int index = [stringOrder indexOfObject:a.assetID];
a.sortOrder = index;
}
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortOrder" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [items sortedArrayUsingDescriptors:sortDescriptors];
Parallel Processing:
Results (quad core):
1. sortme:95 sortby:852345 sorted:95 time:0.052576
2. sortme:54248 sortby:852345 sorted:54243 time:0.264660
-(NSArray *)sortArray:(NSArray *)sortme sortBy:(NSArray *)sortBy{
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
NSSet *sortmeSet = [NSSet setWithArray:sortme];
NSMutableDictionary *sortDictionary = [NSMutableDictionary dictionary];
dispatch_queue_t sortDictionaryThread = dispatch_queue_create("my.sortDictionaryThread", DISPATCH_QUEUE_CONCURRENT);
[sortBy enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([sortmeSet containsObject:obj]){
dispatch_barrier_async(sortDictionaryThread, ^{
sortDictionary[obj] = #(idx);
});
}
}];
__block NSArray *sortedArray = nil;
dispatch_barrier_sync(sortDictionaryThread, ^{
sortedArray = [sortDictionary keysSortedByValueUsingSelector:#selector(compare:)];
});
NSLog(#"sortme:%li sortby:%li sorted:%li time:%f",sortme.count,sortBy.count,sortedArray.count, CFAbsoluteTimeGetCurrent() - time);
return sortedArray;
}

How can I retrieve all the contents of an NSDictionary?

I want to select and retrieve all the contents from an NSDictionary. I have a structure like this
- (void)viewDidLoad {
[super viewDidLoad];
listaOggetti = [[NSMutableArray alloc] init];
NSArray *arrayOne = [NSArray arrayWithObjects: #"First",#"Second",#"Third", nil];
NSArray *sortedOne = [arrayOne sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSDictionary *dictOne = [NSDictionary dictionaryWithObject:sortedOne forKey:#"Elementi"];
NSArray *arrayTWo = [NSArray arrayWithObjects:#"First1",#"Second1" ..., nil];
NSArray *sortedTwo = [arrayTwo sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSDictionary *dictTwo = [NSDictionary dictionaryWithObject:sortedTWo forKey:#"Elementi"];
NSArray *arrayThree = [NSArray arrayWithObjects:#"First2",#"Second2" ... , nil];
NSArray *sortedThree = [arrayThree sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSDictionary *dictThree = [NSDictionary dictionaryWithObject:sortedThree forKey:#"Elementi"];
[listaOggetti addObject:dictOne];
[listaOggetti addObject:dictTwo];
[listaOggetti addObject:dictThree];
}
And I want to retrieve all the objects for the key #"Elementi" (should be around 45) in order to add them in another array, like:
NSDictionary *dict = [listaOggetti objectAtIndex:indexPath.section];
NSArray *array = [dict objectForKey:#"Elementi"];
cellValue = [array objectAtIndex:indexPath.row] ;
(With this, dict is only 9 objects filled in my project).
At the end, the *array should be around 45 objects filled. I tried with allValues, but didn't work.
How can I fix it?
The easiest is to do this in -viewDidLoad:
NSMutableArray *allObjects = [[NSMutableArray alloc] init];
[allObjects addObjectsFromArray:sortedOne];
[allObjects addObjectsFromArray:sortedTwo];
[allObjects addObjectsFromArray:sortedThree];
Alternately, you can get them from the dictionaries in a similar fashion:
NSMutableArray *allObjects = [[NSMutableArray alloc] init];
[allObjects addObjectsFromArray:[[listaOggetti objectAtIndex:0] objectForKey#"Elementi"];
[allObjects addObjectsFromArray:[[listaOggetti objectAtIndex:1] objectForKey#"Elementi"];
[allObjects addObjectsFromArray:[[listaOggetti objectAtIndex:2] objectForKey#"Elementi"];
What you are failing to understand is that listaOggetti is an NSMutableArray containing three objects. When you call
NSDictionary *dict = [listaOggetti objectAtIndex:indexPath.section];
the result is that dict is a single dictionary, one of the three objects in listaOggetti. Therefore when you call
NSArray *array = [dict objectForKey:#"Elementi"];
the result is that array is the object for the key #"Elementi" of that one single dictionary dict. Your code makes no attempt to combine the three DIFFERENT dictionaries or to combine the three arrays, each set as objectForKey:#"Elementi" for the three DIFFERENT dictionaries.
If you want one array that is the concatenation of all three different arrays, then use one of the snippets provided above. In both of these snippets, the result is that allObjects is an NSMutableArray containing all three arrays, in order.