Compare two arrays with the same value but with a different order - objective-c

I have 2 nsarray, with the same values but in different order.
NSArray * array1 = {0,1,2,3}
NSArray * array2 = {2,3,1,0}
I need a method to determinate if two arrays have the same values in a different order.
Kind of
-(BOOL) isSameValues:(NSArray*)array1 and:(NSArray*)array2;

You can use NSCountedSet for that purpose:
- (BOOL)isSameValues:(NSArray*)array1 and:(NSArray*)array2
{
NSCountedSet *set1 = [NSCountedSet setWithArray:array1];
NSCountedSet *set2 = [NSCountedSet setWithArray:array2];
return [set1 isEqualToSet:set2];
}
NSCountedSet is a collection of different objects, where each object has an associated counter with it. Therefore the result for
NSArray *array1 = #[#0,#1,#2,#3];
NSArray *array2 = #[#2,#3,#1,#0];
is YES, but for
NSArray *array1 = #[#1,#1,#3,#3];
NSArray *array2 = #[#3,#3,#3,#1];
the result is NO.

Update: this will not work if arrays have duplicate elements!
You could create two NSSets with those arrays and the compare them.
NSArray * array1 = #[#0,#1,#2,#3];
NSArray * array2 = #[#2,#3,#1,#0];
NSSet *set1 = [NSSet setWithArray:array1];
NSSet *set2 = [NSSet setWithArray:array2];
NSLog(#"result %#", [set1 isEqualToSet:set2] ? #"YES" : #"NO");

if ([[NSSet setWithArray:array1] isEqualToSet:[NSSet setWithArray:array2]]) {
// the objects are the same
}

Take total no of elements. Have a counter. And put double 'for loop' to parse through each and every element of each other. Increment the counter at each matching.
Note : This is valid when all elements are unique.
If different or you don't know, sort them and match one to one.

An other way would be to use a NSHashTable.
- (BOOL)array:(NSArray *)array1 containsTheSameObjectsAsArray:(NSArray *)array2 {
if (array1.count != array2.count) {
return NO;
}
NSHashTable *table = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory
capacity:array1.count];
for (NSObject *object in array1) {
[table addObject:object];
}
for (NSObject *object in array2) {
if (![table containsObject:object]) {
return NO;
}
}
return YES;
}
Note that NSHashTable requires iOS 6+

Related

NSDictionary and comparing SetObjects

Hi I'm wondering how to compare the contents of the setObjects stored with keys in an NSDictionary: Here's my code I'm working with:
[INNumbers setObject:#"0,1,2,3,4,5,6,7" forKey:#"0"];
[INNumbers setObject:#"4,5,6,7,8,9,10,11" forKey:#"1"];
I'm wonder how I would compare the 2 keys and find if there are similar numbers like if 7 occurs in both?
//this will get the numbers in the keys for me
id number1 = [INNumbers objectForKey:#"0"];
id number2 = [INNumbers objectForKey:#"1"];
but I'm not sure how to compare what number1 and number2 retrieve.
If I was not force to use strings, I would do it this way:
NSArray *nums0 = #[#1,#2,#3];
NSArray *nums1 = #[#3,#4,#5];
NSMutableSet *intersection = [NSMutableSet setWithArray:nums0];
[intersection intersectSet:[NSSet setWithArray:nums1]];
NSArray *numsInCommon = [intersection allObjects];
numsInCommon will contain #3 that is the number that is in both arrays
In your example, the values stored in the dictionary are string literals, which are hard to parse and test for membership tests repeatedly. Perhaps you wanted to use NSSets:
#import <Foundation/Foundation.h>
int main()
{
NSSet *numbers1 = [NSSet setWithObjects:#0, #1, #2, nil];
NSSet *numbers2 = [NSSet setWithObjects:#0, #4, #5, nil];
if([numbers1 intersectsSet : numbers2]) {
NSLog(#"The two sets have at least one element in common!");
/* Let's obtain the intersection: */
NSSet *common = [numbers1 objectsPassingTest:^BOOL(id obj, BOOL *stop) {
if ([numbers2 containsObject:obj]) {
return YES;
} else {
return NO;
}
}];
for (id o in common) {
NSLog(#"%#", o);
}
}
return 0;
}

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 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 to extract unique objects from multiple arrays

EDIT:
I have two different arrays with some repeated strings and i want to create a new array with the only the unique strings.
For instance, take these two arrays:
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Result should be an array with objects "b", and "d"
// since they are the only two that are not repeated in the other array.
EDIT:
// Your starting arrays
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Create two new arrays that only contain the objects
// which are not in the other array:
NSMutableArray *uniqueElementsInArray1 = [array1 mutableCopy];
[uniqueElementsInArray1 removeObjectsInArray:array2];
NSMutableArray *uniqueElementsInArray2 = [array2 mutableCopy];
[uniqueElementsInArray2 removeObjectsInArray:array1];
// Combine the two arrays.
// Result contains objects #"b" and #"d":
NSArray *result = [uniqueElementsInArray1 arrayByAddingObjectsFromArray:uniqueElementsInArray2];
For this you just declare one another temp NSMutableArray . Retrieve whatever data u have from your original array say objectArray. Check whether the temp array have that or not and put it into the temp array. Just refer following code:
for(NSString *str in objectArray)
{
if(![tempArray containsObject:str])
{
[tempArray addObject:str];
}
}
After this u can continue to use tempArray or put tempArray into objectArray if you want to use objectArray further.I think this should work for you.
You can use NSSet as a filter (think of Venn Diagrams in your head):
NSArray *array1 = #[#1,#2,#3,#4,#2,#3];
NSArray *array2 = #[#3,#4,#5,#6,#4,#6];
NSSet *set1 = [NSSet setWithArray:array1]; // [1,2,3,4]
NSSet *set2 = [NSSet setWithArray:array2]; // [3,4,5,6]
METHOD 1 (my favorite):
NSMutableSet *mSet1 = [set1 mutableCopy];
NSMutableSet *mSet2 = [set2 mutableCopy];
[mSet1 minusSet:set2]; // mSet1 = [1,2]
[mSet2 minusSet:set1]; // mSet2 = [5,6]
[mSet1 unionSet:mSet2]; // mSet1 = [1,2,5,6], only the unique elements.
// Now just put it in an immutable collections with a self-docu name...
NSArray *arrayOfUniqueness = [setOfUniqueElementsOnly allObjects];
METHOD 2 (more explicit test, no need for Venn Diagrams):
NSSet *setOfObjsUniqueTo1 = [set1 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set2 containsObject:obj];
}]; // [1,2]
NSSet *setOfObjsUniqueTo2 = [set2 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set1 containsObject:obj];
}]; // [5,6]
NSMutableSet *oneSetToRuleThemAll = [NSMutableSet setWithSet:setOfObjsUniqueTo1];
// [1,2]
[oneSetToRuleThemAll unionSet:setOfObjsUniqueTo2]; // [1,2,5,6]
// Or as an array:
NSArray *anotherArrayOfUniqueness = [oneSetToRuleThemAll allObjects];
METHOD 3 (eschews NSSet, but I would not seat this code opposite the Queen of England at a formal dinner -- it is inelegant):
NSMutableArray *mArray1 = [NSMutableArray new];
NSMutableArray *mArray2 = [NSMutableArray new];
NSIndexSet *uniqueIndexes1 = [array1 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array2 containsObject:obj];
}]; // [0,1,4] (b/c #1 and #2 are unique to array1)
[uniqueIndexes1 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray1 addObject:array1[idx]];
}]; // #[#1,#2,#2]
NSIndexSet *uniqueIndexes2 = [array2 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array1 containsObject:obj];
}]; // [2,3,5] (b/c #5 and #6 are unique to array2)
[uniqueIndexes2 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray2 addObject:array2[idx]];
}]; // #[#5,#6,#6]
NSArray *unionArray = [array1 arrayByAddingObjectsFromArray:array2];
// #[#1,#2,#2,#5,#6,#6]
NSArray *yetAnotherArrayOfUniqueness = [[NSSet setWithArray:unionArray] allObjects];
// #[#1,#2,#5,#6]
Not the questioner's question, but to get an array with duplicates removed (i.e., where each element is unique), similar magic can be done:
//given...
NSArray *arr1 = #[#"a", #"b", #"c"];
NSArray *arr2 = #[#"b", #"c", #"d"];
//...make a single array to rule them all:
NSArray *temp = [arr1 arrayByAddingObjectsFromArray:arr2];
//[a,b,c,b,c,d]
//Make an NSSet from the two:
NSSet *filterSet = [NSSet setWithArray:temp]; // Set has: a,b,c,d
//Finally, transmogrify that NSSet into an NSArray:
NSArray *arrayOfUniqueness = [filterSet allObjects]; // [a,b,c,d]
As per the Apple Docs (emphasis added):
+setWithArray:
Creates and returns a set containing a uniqued collection of the objects contained in a given array.
UPDATE: And see here for a similar question: Remove all strings with duplicates in an NSArray
use Set as a filter, example:
String[] arr = {"a","a","b"};
Object[] uniqueArr = (Object[])new HashSet<String>(Arrays.asList(arr)).toArray();

Better solution for this 2x fast-enumeration?

I'm looping through an array and comparing the objects tag property in this array with the objects in another array.
Here's my code:
NSArray *objectsArray = ...;
NSArray *anotherObjectArray = ...;
NSMutableArray *mutableArray = ...;
for (ObjectA *objectA in objectsArray) {
for (ObjectZ *objectZ in anotherObjectArray) {
if ([objectA.tag isEqualToString:objectZ.tag]) {
[mutableArray addObject:objectA];
}
}
}
Is there a better way to do this?
Please note the tag property is not an integer, so have to compare strings.
You can do this by iterating over each array once, rather than nesting:
NSMutableSet *tagSet = [NSMutableSet setWithCapacity:[anotherObjectArray count]];
for(ObjectZ *objectZ in antherObjectArray) {
[tagSet addObject:objectZ.tag];
}
NSMutableArray *output = [NSMutableArray mutableArray];
for(ObjectA *objectA in objectsArray) {
if([tagSet containsObject:objectA.tag]) {
[output addObject:objectA];
}
}
May be you can use [NSArray filteredArrayUsingPredicate:]; - http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
But you may have to tweak for property tag yourself.
NSArray *objectsArray = [NSArray arrayWithObjects:#"Miguel", #"Ben", #"Adam", #"Melissa", nil];
NSArray *tagsArray = [NSArray arrayWithObjects:#"Miguel", #"Adam", nil];
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF IN %#", tagsArray];
NSArray *results = [objectsArray filteredArrayUsingPredicate:sPredicate];
NSLog(#"Matched %d", [results count]);
for (id a in results) {
NSLog(#"Object is %#", a);
}
Hope this helps
Well, the simplest change (as there can only be one match per objectA) then you could do a break after your [mutableArray addObject:objectA]. When a match occurs, that would reduce the inner loop by 50%.
More dramatically, if you're doing this a lot and the order of anotherObjectArray doesn't matter, would be to invert your anotherObjectArray data structure and use a dictionary, storing the objects by tag. Then you just iterate over objectA asking if its tag is in the dictionary of ObjectZs.
Thanks for all the answers. While I have accepted the NSMutableSet solution, I actually ended up going with the following, as it turned out it was a tiny bit faster:
NSMutableDictionary *tagDictionary = [NSMutableDictionary dictionaryWithCapacity:[anotherObjectArray count]];
for (ObjectZ *objectZ in anotherObjectArray) {
[tagDictionary setObject:objectZ.tag forKey:objectZ.tag];
}
for (ObjectA *objectA in objectsArray) {
if ([tagDictionary objectForKey:objectA.tag]) {
[direction addObject:objectA];
}
}