Objective-C: Count the number of times an object occurs in an array? - objective-c

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

Related

sort a dictionary by a key from an inner dictionary

i have a dictionary which i want to sort it according to a key from an inner dictionary. Each key in the super dictionary has a dictionary as value. As an example to illustrate what i'm talking about, here is a super dictionary with inner dictionaries respectively to each key.
{
key1 = {count = 2},
key2 = {count = 1}
}
thus the count key has to be the key used for the sorting. For now i know only how to sort arrays and i didn't encounter sorting dictionaries before. Any help will be appreciated.
get the array out of dictionary & sort it
NSArray* values = [myDict allValues];
NSArray* sortedValues = [values sortedArrayUsingSelector:#selector(comparator)];
another way to do it to make NSSortDescriptor
keyDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"count" ascending:YES] autorelease];
sortDescriptors = [NSArray arrayWithObject:keyDescriptor];
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
I pulled the code from Sort Descriptor Programming Topics. Also, Key-Value Coding comes into play, in that sortedArrayUsingDescriptors: will send a valueForKey: to each element in myArray, and then use standard comparators to sort the returned values.
Here's one way:
NSDictionary * dict1 = [[NSDictionary alloc]initWithObjectsAndKeys:[NSNumber numberWithInt:2], #"count", nil];
NSDictionary * dict2 = [[NSDictionary alloc]initWithObjectsAndKeys:[NSNumber numberWithInt:3], #"count", nil];
NSArray * arrayOfDictionaries = [[NSArray alloc]initWithObjects:dict1, dict2, nil];
NSArray * sortedArray = [arrayOfDictionaries sortedArrayUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
// are points equal?
if ([obj1[#"count"] intValue] != [obj2[#"count"] intValue]) {
// points not equal, compare points
if ([obj1[#"count"] intValue] > [obj2[#"count"] intValue])
return (NSComparisonResult)NSOrderedAscending;
else
return (NSComparisonResult)NSOrderedDescending;
}
else {
return (NSComparisonResult)NSOrderedSame;
}
}];
Let me know how it goes!
Okay, just to add to what others had posted (Thank you!) i also found my own solution which goes like this:
NSArray *keysByFrequency = [object keysSortedByValueUsingComparator:^NSComparisonResult(NSDictionary* obj1, NSDictionary* obj2) {
return [obj1[#"count"] compare:obj2[#"count"]];
}];
NSMutableArray *tags = [[NSMutableArray alloc] init];
for (int i = keysByFrequency.count-1; i >= 0; i--) {
[tags addObject:#{#"tag" : keysByFrequency[i], #"count" : object[keysByFrequency[i]][#"count"], #"type" : object[keysByFrequency[i]][#"type"]}];
}

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 with strings that contain numbers?

I have a NSMutableArray and it has the users high scores saved into it. I want to arrange the items numerically (the numbers are stored in NSStrings.)Example:4,2,7,8To2,4,7,8What is the simplest way to do this if the data is stored in NSStrings?
This code will do it:
//creating mutable array
NSMutableArray *myArray = [NSMutableArray arrayWithObjects:#"4", #"2", #"7", #"8", nil];
//sorting
[myArray sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
return [str1 compare:str2 options:(NSNumericSearch)];
}];
//logging
NSLog(#"%#", myArray);
It uses blocks, make sure your target OS supports that (It's 4.0 for iOS and 10.6 for OSX).
This code works. I tried it:
NSMutableArray *unsortedHighScores = [[NSMutableArray alloc] initWithObjects:#"4", #"2", #"7", #"8", nil];
NSMutableArray *intermediaryArray = [[NSMutableArray alloc] init];
for(NSString *score in unsortedHighScores){
NSNumber *scoreInt = [NSNumber numberWithInteger:[score integerValue]];
[intermediaryArray addObject:scoreInt];
}
NSArray *sortedHighScores = [intermediaryArray sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"%#", sortedHighScores);
The output is this:
2
4
7
8
If you have any questions about the code, just ask in the comments. Hope this helps!
The NSMutableArray method sortUsingSelector: should do it:
[scoreArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)]
should do it.
If the array is of nsdictionaries conaining numeric value for key number
isKeyAscending = isKeyAscending ? NO : YES;
[yourArray sortUsingComparator:^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
NSString *str1 = [obj1 objectForKey:#"number"];
NSString *str2 = [obj2 objectForKey:#"number"];
if(isKeyAscending) { //ascending order
return [str1 compare:str2 options:(NSNumericSearch)];
} else { //descending order
return [str2 compare:str1 options:(NSNumericSearch)];
}
}];
//yourArray is now sorted
The answer from Darshit Shah make it smootly
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc]initWithKey:#"rank" ascending:YES selector:#selector(localizedStandardCompare:)];

How to exclude previously randomly selected NSArrays in loop

I'm new to Objective-C and I'm trying to create a simple dictionary style app for personal use. Right now I'm attempting to make a loop that prints randomly selected NSArrays that have been added to an NSDictionary. I'd like to print each array only once. Here is the code I'm working with:
NSArray *catList = [NSArray arrayWithObjects:#"Lion", #"Snow Leopard", #"Cheetah", nil];
NSArray *dogList = [NSArray arrayWithObjects:#"Dachshund", #"Pitt Bull", #"Pug", nil];
...
NSMutableDictionary *wordDictionary = [[NSMutableDictionary alloc] init];
[wordDictionary setObject: catList forKey:#"Cats"];
[wordDictionary setObject: dogList forKey:#"Dogs"];
...
NSInteger keyCount = [[wordDictionary allKeys] count];
NSInteger randomKeyIndex = arc4random() % keyCount;
int i = keyCount;
for (i=i; i>0; i--) {
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
NSMutableArray *randomlySelectedArray = [wordDictionary objectForKey:randomKey];
NSLog(#"%#", randomlySelectedArray);
}
This code prints the same array "i" times. Any pointers on how to exclude previously printed arrays from being printed again?
I'm wondering if removeObjectForKey: could be of any use.
You just need to re-calculate the random key index every time you go through the loop, and then, as you suggest, use removeObjectForKey:.
Something like this:
NSArray *catList = [NSArray arrayWithObjects:#"Lion", #"Snow Leopard", #"Cheetah", nil];
NSArray *dogList = [NSArray arrayWithObjects:#"Dachshund", #"Pitt Bull", #"Pug", nil];
//...
NSMutableDictionary *wordDictionary = [[NSMutableDictionary alloc] init];
[wordDictionary setObject: catList forKey:#"Cats"];
[wordDictionary setObject: dogList forKey:#"Dogs"];
//...
while ([wordDictionary count] > 0) {
NSInteger keyCount = [wordDictionary count];
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
NSMutableArray *randomlySelectedArray = [wordDictionary objectForKey:randomKey];
NSLog(#"%#", randomlySelectedArray);
[wordDictionary removeObjectForKey: randomKey];
}
In your code, you generate a random randomKeyIndex, then use it without changing its value i times in the loop. So you get i times the same array.
NSInteger randomKeyIndex = arc4random() % keyCount;
// ...
for (i=i; i>0; i--) {
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
// ...
}
As you say removeObjectForKey is an option for you, you can change your code into something like this:
NSInteger keyCount = [[wordDictionary allKeys] count];
for (i=keyCount; i>0; i--) {
NSInteger randomKeyIndex = arc4random() % keyCount;
NSString *randomKey = [[wordDictionary allKeys] objectAtIndex:randomKeyIndex];
NSMutableArray *randomlySelectedArray = [wordDictionary objectForKey:randomKey];
[wordDictionary removeObjectForKey:randomKey];
keyCount--;
NSLog(#"%#", randomlySelectedArray);
}

algorithm to add randomly-generated NSStrings to NSMutableArray

The goal is to generate an NSString chars in length and assign each string to an array. I'm getting stuck on what I need to do with my algorithm to get the correct result. Here's the sample. The result I get is the same randomly generated string added to my array 26 times instead of 26 DIFFERENT strings added.
I've thought about declaring 26 different NSStrings and assigning each result from the algorithm to each string, but that seems inefficient. Thanks for the help.
NSMutableString *string = #"expert";
NSUInteger strLength = [string length];
NSString *letterToAdd;
NSString *finishedWord;
NSMutableString *randomString = [NSMutableString stringWithCapacity: strLength];
NSMutableArray *randomArray = [[NSMutableArray alloc] init];
NSArray *charArray = [[NSArray alloc] initWithObjects: #"a", #"b", #"c", #"d",
#"e", #"f", #"g", #"h", #"i", #"j", #"k", #"l", #"m",
#"o", #"p", #"q", #"r", #"s", #"t", #"u", #"v", #"w",
#"x", #"y", #"z", nil];
for (int a = 0; a < 26; a++) {
for (int i = 0; i < strLength; i++) {
letterToAdd = [charArray objectAtIndex: arc4random() % [charArray count]];
if([randomString length] < strLength) {
[randomString insertString: letterToAdd atIndex: i];
}
finishedWord = randomString;
}
[randomArray addObject: finishedWord];
}
NSLog(#"Random Array count %i, contents: %#", [randomArray count], randomArray);
Here's how I would do it:
#import "NSString+Shuffle.h"
NSString * string = #"expert";
NSUInteger strLength = [string length];
NSString * alphabet = #"abcdefghijklmnopqrstuvwxyz";
NSMutableSet * randomWords = [NSMutableSet set];
while ([randomWords count] < 26) {
NSString * newWord = [alphabet shuffledString];
newWord = [newWord substringToIndex:strLength];
[randomArray addObject:newWord];
}
NSLog(#"Random set count %d, contents: %#", [randomWords count], randomWords);
You'd then need a category on NSString that defines shuffledString. This method would simply take the characters in the string and rearrange them randomly. Decent shuffle algorithms can be found quite easily with Google.
I hope you get the basic idea of how this works. The only modification I made is using an NSSet instead of an NSArray, and what the conditional on the loop is. The eliminates the (slim) possibility of duplicate random words.
Edit: since I'm feeling generous, here's a basic shuffledString implementation:
//NSString+Shuffle.h
#interface NSString (ShuffleAdditions)
- (NSString *) shuffledString;
#end
//NSString+Shuffle.m
#import "NSString+Shuffle.h"
#implementation NSString (ShuffleAdditions)
- (NSString *) shuffledString {
NSMutableString * shuffled = [self mutableCopy];
NSUInteger length = [shuffled length];
for (int i = 0; i < (4*length); ++i) {
NSString * randomChar = [shuffled subStringWithRange:NSMakeRange(arc4random() % (length-1), 1)];
[shuffled appendString:randomChar];
}
return [shuffled autorelease];
}
#end
You should create a new randomString each time:
NSMutableString *string = #"expert";
NSUInteger strLength = [string length];
NSString *letterToAdd;
NSString *finishedWord;
//NSMutableString *randomString = [NSMutableString stringWithCapacity: strLength];
NSMutableArray *randomArray = [[NSMutableArray alloc] init];
NSArray *charArray = [[NSArray alloc] initWithObjects: #"a", #"b", #"c", #"d", #"e", #"f",
#"g", #"h", #"i", #"j", #"k", #"l", #"m", #"o", #"p", #"q", #"r", #"s",
#"t", #"u", #"v", #"w", #"x", #"y", #"z", nil];
for (int a = 0; a < 26; a++) {
NSMutableString *randomString = [NSMutableString stringWithCapacity: strLength];
for (int i = 0; i < strLength; i++) {
letterToAdd = [charArray objectAtIndex: arc4random() % [charArray count]];
//if([randomString length] < strLength) {
[randomString insertString: letterToAdd atIndex: i];
//}
//finishedWord = randomString;
}
//[randomArray addObject: finishedWord];
[randomArray addObject: randomString];
}
NSLog(#"Random Array count %i, contents: %#", [randomArray count], randomArray);
You're adding the same object to your array every time through the loop, and overwriting it as you go. You mentioned:
I've thought about declaring 26 different NSStrings and assigning each
result from the algorithm to each string...
And that is indeed exactly what you need to do. Moving the initialization of randomString into the loop will solve your problem (getting a new NSMutableString on every loop iteration, rather than using a single object). Change the definition of randomString to a simple type definition:
NSMutableString *randomString;
and then in your outer loop, add this line:
randomString = [NSMutableString stringWithCapacity:strLength];
You shouldn't need to change any of the rest of your code.