Comparing Two Arrays - iphone

I have two NSArrays, what I'm looking to do is to compare two arrays which contain strings, find the similarities and create the first array again but so they have no similarities.
Just for an example something like.
Two Arrays:
NSArray *arrayOne = [NSArray arrayWithObjects:#"TD1", #"TD2", #"TD3", nil];
NSArray *arrayTwo = [NSArray arrayWithObjects:#"Blah", #"String", #"TD2", nil];
Outcome:
NSArray *arrayOne = [NSArray arrayWithObjects:#"TD1", #"TD2", #"TD3", nil];
NSArray *arrayOneCopy = [NSArray arrayWithObjects:#"TD1", #"TD3", nil];
NSArray *arrayTwo = [NSArray arrayWithObjects:#"Blah", #"String", #"TD2", nil];

NSMutableArray *arrayOneCopy = [NSMutableArray arrayWithArray:arrayOne];
[arrayOneCopy removeObjectsInArray:arrayTwo];

Use NSMutableSet:
NSMutableSet *setOne = [NSMutableSet setWithArray: arrayOne];
NSMutableSet *setTwo = [NSMutableSet setWithArray: arrayTwo];
[setOne minusSet: setTwo];
NSArray *arrayOneResult = [setOne allObjects];
(strictly speaking, setTwo doesn't have to be mutable, can also be an NSSet (which performs better))
Or use NSArray as the other person answered -- that works, too.
Which one works depends entirely on your data set size. For small sets of data, the array solution works fine. For larger sets, NSSet will be much more efficient in that membership tests are a hash check and not a linear search.
Measure and use the one that works best.

- (NSInteger)countOfDifferentObjects:(NSArray *)anotherArray {
NSSet * s = [NSSet setWithArray:self];
NSMutableSet * s1 = [NSMutableSet setWithSet:s];
NSSet * s2 = [NSSet setWithArray:anotherArray];
[s1 unionSet:s2];
[s1 minusSet:s];
return [s1 count];
}
This returns the number of different objects between two arrays (comparison is with isEqual)
array1 = #"A", #"B", #"C"
array2 = #"B", #"D", #"Z"
returns 2 (D and Z are different)

Related

i had 2 arrays with objects and the same nameobject should be cancelled only one time

I have input as two arrays shown below
NSArray *array1=[[NSArray alloc]initWithObjects:#"1",#"2",#"3", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"1",#"2",#"1", nil];
the output should resemble like this.
the same element should be cancelled only one time.
NSArray *array3=[[NSArray alloc]initWithObjects:#"1",#"2", nil];
THANKS IN ADVANCE.....
NSArray *array1 = #[#"1",#"2",#"3"];
NSArray *array2 = #[#"1",#"2",#"1"];
NSMutableSet *allElemets = [NSSet setWithArray:array1];
[allElemets addObjectsFromArray:array2];
This will return you all elements without duplicates.
In this case it will be
#"1",#"2",#"3"
Edit:
This will return the intersection of the arrays
NSMutableSet *set1 = [NSMutableSet setWithArray:array1];
NSSet *set2 = [NSSet setWithArray:array2];
[set1 intersectSet:set2];
Use NSCountedSet for the above situation
NSMutableArray *array1=[[NSMutableArray alloc]initWithObjects:#"r",#"a",#"r",#"r",#"r", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"b",#"c",#"r", nil];
NSMutableSet *setOne = [NSMutableSet setWithArray: array1];
NSSet *setTwo = [NSSet setWithArray: array2];
[setOne unionSet:setTwo];
NSArray *arrayOneResult = [setOne allObjects];
NSLog(#"%#",arrayOneResult);
NSMutableArray *resultArray = [[NSMutableArray alloc]init];
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:arrayOneResult];
for (id item in set)
{
NSCountedSet *set1 = [[NSCountedSet alloc] initWithArray:array1];
NSCountedSet *set2 = [[NSCountedSet alloc]initWithArray:array2];
int diff = abs([set1 countForObject:item] - [set2 countForObject:item]);
for (int i = 0 ;i < diff ;i++ ) {
[resultArray addObject:item];
}
}
NSLog(#"the array : %#",resultArray );
f you are fine with sets instead of arrays, you can use NSMutableSet instead of NSArray. NSMutableSet has nice methods like intersectSet: and minusSet:
if([[array1 objectAtIndex:i] isEqualToString:[array2 objectAtIndex:i]])
{
[array2 removeObjectAtIndex: i];
NSLog(#"same element removed.");
}
array3 = [firstArray arrayByAddingObjectsFromArray:secondArray];
or
NSMutableSet *set = [NSMutableSet setWithArray:array1];
[set addObjectsFromArray:array2];
array3 = [set allObjects];
Two arrays are compared and duplicate values are removed, you get your values.
Here tHe Code goes
EDIt: This WOuld remove the Duplicate Value add Unique value.
NSArray *array1=[[NSArray alloc]initWithObjects:#"1",#"2",#"3", nil];
NSArray *array2=[[NSArray alloc]initWithObjects:#"1",#"2",#"1", nil];
//Here Create nEw Array with Arra1
NSMutableArray * newArray =[[NSMutableArray alloc] initWithArray:array1];
for(int index=0; index<[array2 count];index++)
{
id object =[array2 objectAtIndex:index];
if(![newArray containsObject:object])//this methods Returns YES/NO
{
[newArray addObject: object];
}
}

Sorting two NSMutableArrays by 'nearest distance' first

I have two arrays, both full of NSString objects like this:
NSMutableArray *titles = [[NSMutableArray alloc] initWithObjects:#"Title1", #"Title2", #"Title3", #"Title4", #"Title5", nil];
NSMutableArray *distances = [[NSMutableArray alloc] initWithObjects:#"139.45", #"23.78", #"347.82", #"10.29", #"8.29", nil];
How can I sort both arrays by the nearest distance first?
So the results would be like this:
titles = #"Title5", #"Title4", #"Title2", #"Title1", #"Title3"
distances = #"8.29", #"10.29", #"23.78", #"139.45", #"347.82"
I understand that NSSortDescriptor can be used in these circumstances but after looking through the documentation, I am still unsure about how.
I would sort the distances this way...
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSArray *sortedDistances = [listItem sortedArrayUsingComparator: ^(id a, id b) {
NSNumber *aNum = [f numberFromString:a];
NSNumber *bNum = [f numberFromString:b];
return [aNum compare:bNum];
}];
I can't think of a particularly quick way to get the associated titles sorted, but this should work ...
NSMutableArray *sortedTitles = [NSMutableArray array];
NSDictionary *distanceTitle = [NSDictionary dictionaryWithObjects:titles forKeys:distances];
for (NSString *distance in sortedDistances) {
NSString *associatedTitle = [distanceTitle valueForKey:distance];
[sortedTitles addObject:associatedTitle];
}
You can use an NSComparator block and use NSArray's sortedArrayUsingComparator method. On that block, you will receive two objects to compare, and base on the comparison result, you can use NSMutableArray exchangeObjectAtIndex:withObjectAtIndex: method to change the values of titles.
Here is a sample how I sort an array of dictionaries by distance value:
-(void)reorderByDistance {
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:#"distance" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
self.contentArray = [self.contentArray sortedArrayUsingDescriptors:sortDescriptors];
}
And my dictionary looks like this:
NSDictionary *dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:#"1", #"id", #"Business #1", #"name", #"This business does some pretty remarkable things", #"description", #"Alley Bar", #"category", #"1.2", #"distance", nil];
One approach would be to create a dictionary mapping titles to distances, sort the distances, and then iterate through the distances to recreate the titles:
NSMutableArray *titles = [[NSMutableArray alloc] initWithObjects:#"Title1", #"Title2", #"Title3", #"Title4", #"Title5", nil];
NSMutableArray *distances = [[NSMutableArray alloc] initWithObjects:#"139.45", #"23.78", #"347.82", #"10.29", #"8.29", nil];
//Create a map of current titles to distances
NSDictionary *titleDistanceMap = [NSDictionary dictionaryWithObjects:titles forKeys:distances];
//Need to sort the strings as numerical values
[distances sortUsingComparator:^(NSString *obj1, NSString *obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//Now re-populate the titles array
[titles removeAllObjects];
for (NSString *distance in distances){
[titles addObject:[titleDistanceMap objectForKey:distance]];
}

iPhone: counting unique items in an array [duplicate]

I need to perform what I feel is a basic function but I can't find any documentation on how to do it. Please help!
I need to count how many times a certain object occurs in an array. See example:
array = NSArray arrayWithObjects:#"Apple", #"Banana", #"Cantaloupe", #"Apple", #"DragonFruit", #"Eggplant", #"Apple", #"Apple", #"Guava",nil]retain];
How can I iterate through the array and count the number of times it finds the string #"Apple"?
Any help is appreciated!
One more solution, using blocks (working example):
NSInteger occurrences = [[array indexesOfObjectsPassingTest:^(id obj, NSUInteger idx, BOOL *stop) {return [obj isEqual:#"Apple"];}] count];
NSLog(#"%d",occurrences);
As #bbum said, use an NSCounted set. There is an initializer thet will convert an array directly into a counted set:
NSArray *array = [[NSArray alloc] initWithObjects:#"A", #"B", #"X", #"B", #"C", #"D", #"B", #"E", #"M", #"X", nil];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:array];
NSLog(#"%#", countedSet);
NSLog output:
(D [1], M [1], E [1], A [1], B [3], X [2], C [1])
Just access items:
count = [countedSet countForObject: anObj]; ...
A Simple and specific answer:
int occurrences = 0;
for(NSString *string in array){
occurrences += ([string isEqualToString:#"Apple"]?1:0); //certain object is #"Apple"
}
NSLog(#"number of occurences %d", occurrences);
PS: Martin Babacaev's answer is quite good too. Iteration is faster with blocks but in this specific case with so few elements I guess there is no apparent gain. I would use that though :)
Use an NSCountedSet; it'll be faster than a dictionary and is designed to solve exactly that problem.
NSCountedSet *cs = [NSCountedSet new];
for(id anObj in someArray)
[cs addObject: anObj];
// then, you can access counts like this:
.... count = [cs countForObject: anObj]; ...
[cs release];
Just came across this pretty old question. I'd recommend using a NSCountedSet:
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:array];
NSLog(#"Occurrences of Apple: %u", [countedSet countForObject:#"Apple"]);
I would encourage you to put them into a Dictionary (Objective C's version of a map). The key to the dictionary is the object and the value should be the count. It should be a MutableDictionary of course. If the item is not found, add it and set the count to 1.
- (int) numberOfOccurrencesForString:(NSString*)needle inArray:(NSArray*)haystack {
int count = 0;
for(NSString *str in haystack) {
if([str isEqualToString:needle]) {
count++;
}
}
return count;
}
I up-voted Rob's answer, but I wanted to add some code that I hope will be of some assistance.
NSArray *array = [[NSArray alloc] initWithObjects:#"A", #"B", #"B", #"B", #"C", #"D", #"E", #"M", #"X", #"X", nil];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
for(int i=0; i < [array count]; i++) {
NSString *s = [array objectAtIndex:i];
if (![dictionary objectForKey:s]) {
[dictionary setObject:[NSNumber numberWithInt:1] forKey:s];
} else {
[dictionary setObject:[NSNumber numberWithInt:[dictionary objectForKey:s] intValue]+1 forKey:s];
}
}
for(NSString *k in [dictionary keyEnumerator]) {
NSNumber *number = [dictionary objectForKey:k];
NSLog(#"Value of %#:%d", k, [number intValue]);
}
If the array is sorted as in the problem statement then you don't need to use a dictionary.
You can find the number of unique elements more efficiently by just doing 1 linear sweep and incrementing a counter when you see 2 consecutive elements being the same.
The dictionary solution is O(nlog(n)), while the linear solution is O(n).
Here's some pseudo-code for the linear solution:
array = A,B,B,B,B,C,C,D,E,M,X,X #original array
array = array + -1 # array with a dummy sentinel value to avoid testing corner cases.
# Start with the first element. You want to add some error checking here if array is empty.
last = array[0]
count = 1 # you have seen 1 element 'last' so far in the array.
for e in array[1..]: # go through all the elements starting from the 2nd one onwards
if e != last: # if you see a new element then reset the count
print "There are " + count + " " + last elements
count = 1 # unique element count
else:
count += 1
last = e
the complete code with reference to #bbum and #Zaph
NSArray *myArray = [[NSArray alloc] initWithObjects:#"A", #"B", #"X", #"B", #"C", #"D", #"B", #"E", #"M", #"X", nil];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:myArray];
for (NSString *item in countedSet) {
int count = [countedSet countForObject: item];
NSLog(#"the String ' %# ' appears %d times in the array",item,count);
}
Thank you.
If you want it more generic, or you want to count equals/different objects in array, try this:
Sign "!" count DIFFERENT values. If you want SAME values, remove "!"
int count = 0;
NSString *wordToCheck = [NSString string];
for (NSString *str in myArray) {
if( ![str isEqualToString:wordToCheck] ) {
wordToCheck = str;
count++;
}
}
hope this helps the community!
I've used it to add correct number of sections in uitableview!
You can do this way,
NSArray *array = [[NSArray alloc] initWithObjects:#"A", #"B", #"X", #"B", #"C", #"D", #"B", #"E", #"M", #"X", nil];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:array];
NSArray *uniqueStates = [[orderedSet set] allObjects];
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:array];
for(int i=0;i<[uniqueStates count];i++){
NSLog(#"%# %d",[uniqueStates objectAtIndex:i], [countedSet countForObject: [uniqueStates objectAtIndex:i]]);
}
The result is like : A 1

Sorting NSSets of a core data entity - Objective-C

I would like to sort the data of a core data NSSet (I know we can do it only with arrays but let me explain...). I have an entity user who has a relationship to-many with the entity recipe. A recipe has the attributes name and id.
I would like to get the data such that:
NSArray *id = [[user.recipes valueForKey:#"identity"] allObjects];
NSArray *name = [[user.recipes valueForKey:#"name"] allObjects];
if I take the object at index 1 in both arrays, they correspond to the same recipe...
Thanks
You need to sort the recipes first:
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]];
NSArray *sortedRecipes = [[recipes allObjects] sortedArrayUsingDescriptors:sortDescriptors];
you can then extract an array of attributes from the sorted recipes array and the results will remain in sorted order:
NSArray *sortedNames = [sortedRecipes valueForKey:#"name"];
NSArray *sortedIdentities = [sortedRecipes valueForKey:#"identity"];
If you want them to be in the same order, then you need to sort them before extracting the values. Example:
NSArray * sortedRecipes = [user.recipes sortedArrayUsingDescriptors:arrayOfSortDescriptors];
NSArray * identities = [sortedRecipes valueForKey:#"identity"];
NSArray * names = [sortedRecipes valueForKey:#"name"];
EDIT
My apologies. I just realized this is an iPhone question, and NSSet doesn't have sortedArrayUsingDescriptors: on the iPhone. However, it's trivial to work around:
NSArray * recipes = [user.recipes allObjects];
NSArray * sortedRecipes = [recipes sortedArrayUsingDescriptors:arrayOfSortDescriptors];
....

Sort Array by Key

I have two arrays used in a small game.
If the player gets a score above a certain value their name & score gets output via
an UILabel.
NSArray *namesArray = [mainArray objectForKey:#"names"];
NSArray *highScoresArray = [mainArray objectForKey:#"scores"];
I need the UILabels to display with the highest score in descending order, with the corresponding name. I've used an NSSortDescriptor to sort the score values numerically.
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"self"
ascending:NO] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedScore = [[NSArray alloc]init];
sortedScore = [scoresArray sortedArrayUsingDescriptors:sortDescriptors];
NSMutableArray *scoreLabels = [NSMutableArray arrayWithCapacity:10];
[scoreLabels addObject:scoreLabel1];
......
NSUInteger _index = 0;
for (NSNumber *_number in sortedScore) {
UILabel *_label = [scoreLabels objectAtIndex:_index];
_label.text = [NSString stringWithFormat:#"%d", [_number intValue]];
_index++;
}
This works well enough as the scores now display in descending order.
The problem is that I need the corresponding name to also display according in the new sorted order.
I cant use the same sort selector and I don't wont to sort them alphabetically, they need
to correspond to the name/score values that were first input.
Thanks in advance
You need to put the name and the score together into a single instance of NSDictionary, and then have an NSArray of those NSDictionary instances. Then when you sort by score, you can pull up the corresponding name.