I know how you can sort an Array by alphabet, but I want to sort my NSMutableArray by the popularity of the objects in it. Imagine an array with the following objects:
B
A
C
B
B
A
I want to sort these objects by popularity and create an array like this:
B
B
B
A
A
C
Is there a good and quick way to do this? Maybe a key for NSSortDescriptor? (couldn't find that)
Thanks
Short and to the point, I used the new syntax for array literals for brevity's sake.
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray *ar = #[#"B", #"A", #"C", #"B", #"B", #"A"];
NSCountedSet *countedSet = [NSCountedSet setWithArray:ar];
NSArray *sorted = [ar sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSInteger diff = [countedSet countForObject:obj1] - [countedSet countForObject:obj2];
if (diff < 0)
return NSOrderedDescending;
else if (diff > 0)
return NSOrderedAscending;
else
return NSOrderedSame;
}];
NSLog(#"%#", ar);
NSLog(#"%#", sorted);
}
return 0;
}
Output
2012-05-08 07:25:18.465 Sort[20434:303] (
B,
A,
C,
B,
B,
A
)
2012-05-08 07:25:18.468 Sort[20434:303] (
B,
B,
B,
A,
A,
C
)
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"theSortKey"
ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
also see this thread
How to sort an NSMutableArray with custom objects in it?
I would use a dictionary which have A,B,C... as the key and set default value to 0
then enumerate the array and increase the count of corresponding key.
finally sort the key array returned by [dict allKeys];
This should work for you.
NSArray *array = [[NSArray alloc] initWithObjects:#"A", #"A", #"B", #"B", #"B", #"C", nil];
NSMutableDictionary *occurenceOfValues = [[NSMutableDictionary alloc] init];
for (id value in array) {
NSNumber *count = [occurenceOfValues objectForKey:value];
[occurenceOfValues setValue:[NSNumber numberWithInt:[count intValue] + 1] forKey:value];
}
NSArray *valuesInOrder = [[[occurenceOfValues keysSortedByValueUsingSelector:#selector(compare:)] reverseObjectEnumerator] allObjects];
NSMutableArray *sortedArray = [[NSMutableArray alloc] initWithCapacity:array.count];
for (id value in valuesInOrder) {
NSNumber *count = [occurenceOfValues objectForKey:value];
for (int i = 0; i < [count intValue]; i++) {
[sortedArray addObject:value];
}
}
NSLog(#"%#", sortedArray);
Console output:
2012-05-08 09:20:15.805 SortArrayPopularity[25206:f803] (
B,
B,
B,
A,
A,
C
)
Related
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];
}
}
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]];
}
I need so sort an array with an array inside, something like this:
NSMutableArray * array_example = [[NSMutableArray alloc] init];
[array_example addObject:[NSMutableArray arrayWithObjects:
string_1,
string_2,
string_3,
nil]
];
how can i order this array by the field "string_1" of the array???
any idea how can i do that?
Thanks
For iOS 4 and later, this is easily done using a comparator block:
[array_example sortUsingComparator:^(NSArray *o1, NSArray *o2) {
return (NSComparisonResult)[[o1 objectAtIndex:0] compare:[o2 objectAtIndex:0]];
}];
If you're interested in how blocks work, you can have a look at Apple's Short Practical Guide to Blocks.
If you wish to support iOS 3.x, you'd have to use a custom comparison function:
NSComparisonResult compareArrayFirstElement(NSArray *o1, NSArray *o2) {
return [[o1 objectAtIndex:0] compare:[o2 objectAtIndex:0]];
}
and then use:
[array_example sortUsingFunction:compareArrayFirstElement context:nil];
You can loop the array objects and call sortedArrayUsingSelector on each sub array, then replaceObjectAtIndex:withObject to inject back into the original array
NSMutableArray * array_example = [[NSMutableArray alloc] init];
[array_example addObject:[NSMutableArray arrayWithObjects:
#"z",
#"a",
#"ddd",
nil]
];
[array_example addObject:[NSMutableArray arrayWithObjects:
#"g",
#"a",
#"p",
nil]
];
NSLog(#"Original Array: %#", array_example);
for(int i = 0; i < [array_example count] ; i++){
[array_example replaceObjectAtIndex:i withObject:[[array_example objectAtIndex:i] sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)]];
// order sub array
}
NSLog(#"Sorted Array: %#", array_example);
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
I have a table view, which has its data source from an array that contains names of people.
Now to make it easy to find people, I want to section the table view so that it has the letter A-Z on the right hand side, just like the Address Book app.
But my current array just contains a collection of NSStrings. How do I split them so that they are grouped by the first letter of the names? Is there any convenient way to do it?
EDIT: If anyone's interested in my final code:
NSMutableArray *arrayChars = [[NSMutableArray alloc] init];
for (char i = 'A'; i <= 'Z' ; i++) {
NSMutableDictionary *characterDict = [[NSMutableDictionary alloc]init];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for (int k = 0; k < [myList count]; k++) {
NSString *currentName = [[friends objectAtIndex:k] objectForKey:#"name"];
char heading = [currentName characterAtIndex:0];
heading = toupper(heading);
if (heading == i) {
[tempArray addObject:[friends objectAtIndex:k]];
}
}
[characterDict setObject:tempArray forKey:#"rowValues"];
[characterDict setObject:[NSString stringWithFormat:#"%c",i] forKey:#"headerTitle"];
[arrayChars addObject:characterDict];
[characterDict release];
[tempArray release];
}
At the end of the function I'll have:
arrayChars [0] = dictionary(headerTitle = 'A', rowValues = {"adam", "alice", etc})
arrayChars[1] = dictionary(headerTitle = 'B', rowValues = {"Bob", etc})
Thank you everyone for your help!
You can use a dictionary to sort them, so create an array with all the letters you want to sort and a array with nil objects to initialize the dictionary
NSArray *names = #[#"javier",#"juan", #"pedro", #"juan", #"diego"];
NSArray *letters = #[#"j", #"p", #"d"];
NSMutableArray *objects = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < [letters count]; ++i)
{
[objects addObject:[[NSMutableArray alloc] init]];
}
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:letters];
Then you must find the first letter if the word and put that word into the corresponding key in the dictionary
for (NSString *name in names) {
NSString *firstLetter = [name substringToIndex:1];
for (NSString *letter in letters) {
if ([firstLetter isEqualToString:letter]) {
NSMutableArray *currentObjects = [dictionary objectForKey:letter];
[currentObjects addObject:name];
}
}
}
To check you can print directly the dictionary
NSLog(#"%#", dictionary);
Then is your work to fill your sections in the tableview using the dictionary