Sorting mutable array by dictionary key - iphone

I have already looked through a few answers using the various sorting methods of NSMutableArray, but for some reason they are not working for me.
I am just trying to sort the mutable array which contains dictionaries by the Delay key within each dictionary. However, the "sorted" array is the exact same as the original array.
By the way, it works fine if I create a dummy mutable array and populate it with dictionaries containing numbers, but for some reason it won't sort this mutable array that I am initializing.
What am I doing wrong?
Here's my code:
playlistCalls = [[NSMutableArray alloc] initWithArray:[currentPlaylist objectForKey:#"Tunes"]];
NSLog(#"original %#", playlistCalls);
NSSortDescriptor *delay = [NSSortDescriptor sortDescriptorWithKey:#"Delay" ascending:YES];
[playlistCalls sortUsingDescriptors:[NSArray arrayWithObject:delay]];
NSLog(#"sorted %#", playlistCalls);
Here's the array containing the dictionaries:
2012-06-04 15:48:09.129 MyApp[57043:f503] original (
{
Name = Test Tune;
Delay = 120;
Volume = 100;
},
{
Name = Testes;
Delay = 180;
Volume = 100;
},
{
Name = Testing;
Delay = 60;
Volume = 100;
}
)
2012-06-04 15:48:09.129 MyApp[57043:f503] sorted (
{
Name = Test Tune;
Delay = 120;
Volume = 100;
},
{
Name = Testes;
Delay = 180;
Volume = 100;
},
{
Name = Testing;
Delay = 60;
Volume = 100;
}
)

The code above is fine when I use NSNumbers in my dictionary. That leads me to believe that the Delay value is stored as strings in your dictionary. What you will need to do then is sort by the strings integerValue.
NSSortDescriptor *delay =
[NSSortDescriptor sortDescriptorWithKey:#"Delay.integerValue" ascending:YES];

Please try the following, it worked with me
NSDictionary *dic;
NSMutableArray *arr = [[NSMutableArray alloc] init];
dic = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:120], #"Delay",
[NSNumber numberWithInt:100], #"Volume",
nil];
[arr addObject:dic];
dic = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:160], #"Delay",
[NSNumber numberWithInt:100], #"Volume",
nil];
[arr addObject:dic];
dic = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:100], #"Delay",
[NSNumber numberWithInt:100], #"Volume",
nil];
[arr addObject:dic];
NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:#"Delay" ascending:YES];
[arr sortUsingDescriptors:[NSArray arrayWithObject:sortDesc]];

Related

Sort an array using array index

I am stuck here......
I have two arrays Arr_title and Arr_distance
I get sorted Arr_distance using this method
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES]autorelease];
sortedFloats = [appDel.Arr_distance sortedArrayUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]];
but problem is that i want to sort Arr_title according to index of sortedFloats array...
thx in advance for help me..........
Try this,
NSDictionary *dict = [NSDictionary dictionaryWithObjects:Arr_title forKeys:Arr_distance];
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES] autorelease];
sortedFloats = [appDel.Arr_distance sortedArrayUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]];
NSMutableArray *sortedTitles = [NSMutableArray array];
for (NSString *key in sortedFloats) {//or just use sortedTitles = [dict objectsForKeys:sortedFloats notFoundMarker:[NSNull null]];
[sortedTitles addObject:[dict valueForKey:key]];
}
NSLog(#"%#",sortedValues);
sortedTitles should give you the array sorted in the same order as sortedFloats.
hey instead of taking two different array take 1 dictionary with key title,distance save that dictionary in array then sort that array So you have got combination together so there is no any problem due to mapping in any sort(by title & by distance).
if i understand your question right , you can use NSDictionary for the same task
NSMutableArray *array = [NSMutableArray arrayWithArray:#[ #{#"title" : #"City1",#"dist" : #5},
# {#"title" : #"City2",#"dist" : #2.3 },
#{#"title" : #"City3",#"dist" : #11.0 },
#{#"title" : #"City4",#"dist" : #1.0},
#{#"title" : #"City5",#"dist" : #.5},
#{#"title" : #"City6",#"dist" : #13 }]];
NSLog(#"dict<%#>",array);
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"dist" ascending:YES];
NSArray *sortarray = [array sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
NSLog(#"sort array = <%#>",sortarray);
I got the solution see following code.........
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES]autorelease];
sortedFloats = [appDel.Arr_distance sortedArrayUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]];
NSLog(#" Sorted Array : %#", sortedFloats);
NSDictionary *dictTitle = [NSDictionary dictionaryWithObjects:Arr_title forKeys:appDel.Arr_distance];
// Sort the second array based on the sorted first array
sortedTitle = [dictTitle objectsForKeys:sortedFloats notFoundMarker:[NSNull null]];
NSLog(#" Sorted Title : %#",sortedTitle);
// Put the two arrays into a dictionary as keys and values
NSDictionary *dictAddress = [NSDictionary dictionaryWithObjects:Arr_address forKeys:appDel.Arr_distance];
// Sort the second array based on the sorted first array
sortedAddress = [dictAddress objectsForKeys:sortedFloats notFoundMarker:[NSNull null]];
NSLog(#" Sorted Address : %#",sortedAddress);
Dont forgot to retain your arrays if use in another method ....

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]];
}

Geting value from NSMutableArray after using sortedArrayUsingDescriptors

I have made a NSDictionary HospitalDictionary which holds latitude,longitude and distances
NSMutableArray *HospitalArray = [NSMutableArray array];
NSDictionary *HospitalDictionary = [[NSDictionary alloc]init];
NSString *LATITUDE = #"latitude";
NSString *LONGITUDE = #"longitude";
NSString *DISTANCE = #"distance";
for (int i=0; i<=100; i++) {
// calculations for coordinates and distance are done ....
HospitalDictionary = [NSDictionary dictionaryWithObjectsAndKeys:latitude, LATITUDE,
longitude, LONGITUDE,[NSNumber numberWithInt:distanceInKm], DISTANCE, nil];
[HospitalArray addObject:HospitalDictionary];
{
Then data is short using following code
// These line are added to short the Dictionary added to Array List with the Key of Distance
NSSortDescriptor *distanceDescriptor = [[NSSortDescriptor alloc] initWithKey:DISTANCE ascending:YES];
id obj;
NSEnumerator * enumerator = [HospitalArray objectEnumerator];
NSArray *descriptors = [NSArray arrayWithObjects:distanceDescriptor, nil];
NSArray *sortedArray = [HospitalArray sortedArrayUsingDescriptors:descriptors];
enumerator = [sortedArray objectEnumerator];
// this will print out the shorted list for DISTANCE
while ((obj = [enumerator nextObject])) NSLog(#"%#", obj);
// this will return the object at index 1
NSLog(#"Selected array is %#",[sortedArray objectAtIndex:1]);
Output of this is -
2012-05-30 08:24:42.784 Hospitals[422:15803]
sorted array is
{
distance = 1;
latitude = "27.736221";
longitude = "85.330095";
}
I want to get only Latitude or Longitude for the ObjectAtIndex:1 for the sortedArray. How can i get the particular value ie. latitude out of sortedArray not the complete list.
To access a property/method of an object in an array, you can use something like this:
NSLog(#"Latitude for object at index 1 is %#",[[sortedArray objectAtIndex:1] latitude]);

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

Algorithm: Keeping count of key/value pair in NSDictionary

Being new to Cocoa, and probably not knowing all of the potential classes available that already have this functionality neatly wrapped in an OO class, here's an algorithm inquiry. What's the best bet to count how many times a particular key occurs in an array of multiple NSDictionary instances?
Essentially my data structure (in this case an NSArray) might contain several NSDictionary instances at any given time, each one having the same keys, potentially different values. Some values repeat. I'd like to be able to know how many times a particular key/value appears. Example:
{
foo => 1,
bar => 2
}
{
foo => 1,
bar => 3
}
{
foo => 2,
bar => 1
}
In this case I'm interested that foo=>1 occured 2 times and foo=>2 occured 1 time. Is building an instance of NSCountedSet the best way to go about this? Perhaps a C linked-list?
You may want to rethink how you are structuring your data. I'd track something like this while adding to the NSArray instead of trying to discover it at a later time. You might create a new class to handle adding and removing the data so that you can keep your own counts of the data.
NSDictionary * dict1 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:1], #"foo",
[NSNumber numberWithInt:2], #"bar", nil];
NSDictionary * dict2 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:1], #"foo",
[NSNumber numberWithInt:3], #"bar", nil];
NSDictionary * dict3 = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:2], #"foo",
[NSNumber numberWithInt:1], #"bar", nil];
NSArray * arrayOfDictionaries = [[NSArray alloc] initWithObjects:
dict1, dict2, dict3, nil];
// count all keys in an array of dictionaries (arrayOfDictionaries):
NSMutableDictionary * countKeys = [[NSMutableDictionary alloc] initWithCapacity:0];
NSCountedSet * counts = [[NSCountedSet alloc] initWithCapacity:0];
NSArray * keys;
NSString * pairString;
NSString * countKey;
for (NSDictionary * dictionary in arrayOfDictionaries)
{
keys = [dictionary allKeys];
for (NSString * key in keys)
{
pairString = [NSString stringWithFormat:#"%#->%#", key, [dictionary valueForKey:key]];
if ([countKeys valueForKey:pairString] == nil)
{
[countKeys setValue:[NSString stringWithString:pairString] forKey:pairString];
}
countKey = [countKeys valueForKey:pairString];
{ [counts addObject:countKey]; }
}
}
NSLog(#"%#", counts);
[counts release];
[countKeys release];
[arrayOfDictionaries release];
[dict1 release];
[dict2 release];
[dict3 release];
NSCountedSet *keyCounts = [NSCountedSet set];
for (NSDictionary *dict in myDictionaries)
[keyCounts unionSet:[NSSet setWithArray:[dict allKeys]]];