I am working with a schedule that specifies times of day, such as 10:30 AM. I do not know the dates, however. I'm going to store these as values in a NSDictionary and would like to deal with them in a straightforward way.
I can't use NSDate, since I don't have a date. At least, not in a straightforward way.
The other way that seems obvious is NSTimeInterval, but that looks like it's probably a source of very subtle errors. I'm thinking in particular of daylight savings time (which is on my mind this week for some reason!).
Other than that, the only things that really spring to mind are keeping it in a formatted string or encoding it in a NSNumber (like hours * 60 + minutes). Both of which would work out fine, of course, but seem like I'm inventing a square wheel where I'm sure there's already a round one somewhere.
What's the least against the grain way of dealing with raw times using Cocoa?
The short answer is that there's no built-in mechanism for storing time-of-day, so just use whatever is most convenient to you. I've used strings in the past using my own encoding and parsing code, (e.g., "22:00") because they're easy to read and debug, but there's nothing wrong with storing seconds or minutes past midnight as you suggest. Just remember that you'll have to do the math yourself.
How ever you do it, you will need separate year, month, day, hour, minute, and second values so that you can construct an NSDate from NSDateComponents, using NSCalendar's -dateFromComponents: method.
And as others have said, you cannot set the time-of-day by adding hours and minutes to an existing NSDate because if you cross a DST boundary you won't get the value you expect. (However, I assume you can still add day and month components without worrying about DST)
So, I guess there's no simple inbuilt way of doing this. It also looks like the only way to get Cocoa to build the time I expect on DST boundaries is with strings.
So for posterity, it looks like I'll be using something like this.
Test harness:
//
// TimeTest.m
//
#import <Foundation/Foundation.h>
#import "Utility.h"
id utility;
void testTime( NSTimeInterval time ) {
id gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar] autorelease];
id oneDay = [[[NSDateComponents alloc] init] autorelease];
[oneDay setDay: 1];
id thisDay = [gregorian dateFromComponents: [gregorian components: (NSEraCalendarUnit | NSYearCalendarUnit)
fromDate: [NSDate date]]];
for (NSInteger dayIdx = 0; dayIdx < 365; ++dayIdx ) {
NSDate *dateTime = [utility timeInSeconds: time
onDate: thisDay];
NSLog( #"%#", dateTime );
thisDay = [gregorian dateByAddingComponents: oneDay
toDate: thisDay
options: 0];
}
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
utility = [[[Utility alloc] init] autorelease];
testTime( ((10 * 60.0) + 0.0) * 60.0 );
testTime( ((9 * 60.0) + 30.0) * 60.0 );
[pool drain];
return 0;
}
Utility header:
//
// Utility.h
//
#import <Foundation/Foundation.h>
#interface Utility : NSObject {
NSCalendar *gregorian;
NSDateFormatter *dateWithoutTimeFormatter, *dateWithTimeFormatter;
}
- (NSDate *)timeInHours: (NSInteger)hours
minutes: (NSInteger)minutes
seconds: (NSInteger)seconds
onDate: (NSDate *)inDate;
- (NSDate *)timeInSeconds: (NSTimeInterval)inTime
onDate: (NSDate *)inDate;
#end
Utility implementation:
//
// Utility.m
//
#import "Utility.h"
#interface Utility()
#property (nonatomic, readwrite, retain) NSCalendar *gregorian;
#property (nonatomic, readwrite, retain) NSDateFormatter *dateWithoutTimeFormatter, *dateWithTimeFormatter;
#end
#implementation Utility
#synthesize gregorian, dateWithoutTimeFormatter, dateWithTimeFormatter;
- (NSDate *)timeInHours: (NSInteger)hours
minutes: (NSInteger)minutes
seconds: (NSInteger)seconds
onDate: (NSDate *)inDate;
{
id timeStr = [[NSMutableString alloc] initWithFormat: #"%02d:%02d:%02d", hours, minutes, seconds];
id dateStr = [dateWithoutTimeFormatter stringFromDate: inDate];
id dateTimeStr = [[NSString alloc] initWithFormat: #"%# %#", dateStr, timeStr];
[timeStr release];
id dateTime = [dateWithTimeFormatter dateFromString: dateTimeStr];
[dateTimeStr release];
return dateTime;
}
- (NSDate *)timeInSeconds: (NSTimeInterval)inTime
onDate: (NSDate *)inDate;
{
NSAssert1( inTime < 24.0 * 3600.0, #"Time %f must be less than 24hrs", inTime );
double temp = inTime;
int hours = rintf(floor( temp / 3600.0 ));
temp -= ( hours * 3600 );
int minutes = rintf(floorf( temp / 60.0 ));
temp -= ( minutes * 60 );
int seconds = rintf( temp );
return [self timeInHours: hours
minutes: minutes
seconds: seconds
onDate: inDate];
}
- (id)init;
{
if (( self = [super init] )) {
self.gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar] autorelease];
self.dateWithoutTimeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateWithoutTimeFormatter setDateFormat: #"yyyy-MM-dd"];
self.dateWithTimeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateWithTimeFormatter setDateFormat: #"yyyy-MM-dd HH:mm:ss"];
}
return self;
}
- (void)dealloc;
{
self.gregorian = nil;
self.dateWithoutTimeFormatter = nil;
self.dateWithTimeFormatter = nil;
[super dealloc];
}
#end
Why bother with a separate unit for this? Well, I've written enough date formatting code to know that constructing NSCalendar and NSDateFormatter on the fly utterly kills performance.
You can use NSDateComponents to store only the components you need (hour and minutes for example), and then use NSCalendar dateByAddingComponents:toDate:options: to create an absolute date reference, when you need using the serialized components and a base date.
Related
I want to "hardcode" an expiration date into my beta code. Right now I manually calculate a unix date and compare that to the current date time:
if([[NSDate date] timeIntervalSince1970]>1422748800) mustHalt = TRUE;
I'd like a way of replacing the 1422748800 with a macro that generates the equivalent number for a date 90 days in the future at compile time.
Any suggestions?
The predefined macro __DATE__ is what you need. Here is a SO question related to this. But maybe you want to use a code like this:
const int daysToExpire = 14;
NSString *compileDate = [NSString stringWithUTF8String:__DATE__];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"MMM d yyyy"];
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[df setLocale:usLocale];
NSDate *expireDate = [df dateFromString:compileDate];
bool isExpired = ([[NSDate date] compare:expireDate] == NSOrderedDescending); // decide for it
quick question I used the following code to retrieve a difference between two timestamps.
The minutes are given to me now in 0.5 hrs.
how do I get that into minutes (NSInteger format)?
NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease]];
[dateFormatter setDateFormat:#"mm:ss"];
NSDate* firstDate = [dateFormatter dateFromString:#"06:00"];
NSDate* secondDate = [dateFormatter dateFromString:#"17:30"];
NSTimeInterval timeDifference = [secondDate timeIntervalSinceDate:firstDate];
I'm using an NSInteger to get the full hours, but can't figure out the minutes... must be too late at night for me :-/
NSInteger hoursBetweenDates = timeDifference / 60;
NSInteger minutesBetweenDates = ??;
NSLog(#"RAW: %f", timeDifference);
NSLog(#"Hours: %i", hoursBetweenDates);
NSLog(#"Minutes: %i", minutesBetweenDates);
Thanks guys! :-D
The NSTimeInterval is in seconds. So minutes would be diff/60.0 and hours would be diff/3600.0.
you could do:
NSTimeInterval timeDifference = [secondDate timeIntervalSinceDate:firstDate];
int minute = (int)timeDifference % 3600 / 60;
This should be simple, but it's proving challenging for me. I'd like to know the best approach to calculating the difference in seconds between [NSDate date] and a future event x seconds from that time. There are several different types of event, and each event may occur several times a day, and at different times, depending what day of the week it happens to be.
What I am trying to do is have the user select an event type from a picker and then set an alarm in Notification Center for the next occurrence of that event based on their selection. I have everything working fine, except for the seconds calculation.
So, for example, let's say it's 9am on a Monday. I'd like to determine how many seconds it would be between now and a user selected event that regularly occurs every Tuesday, Thursday, and Saturday at 10am, 4pm, and 11pm on each day, or on Sunday at 1pm. How would you approach this most efficiently?
When you're talking about a time or date like "next Thursday at 1 PM", that's information that only makes sense in the context of a calendar. NSDate is not going to provide you with much help. It would perhaps be more appropriately named NSPointInTime. It's just a number of seconds that have passed from some earlier, arbitrary reference point in time. It has no notion of weekdays, ante/post meridiem, or even hour of the day.
The two objects that do know about those sorts of thing are NSDateComponents and NSCalendar. Working together, they can create an NSDate from a specification like "next Thursday at 1PM".
You can decompose any date into components using -[NSCalendar components:fromDate:], and you can then use other NSDateComponents objects to perform arithmetic on the individual pieces of information. Find the weekday of today, for example, and its difference from Thursday. Then use -[NSCalendar dateByAddingComponents:toDate:options:] to create a new date based on that offset.
#interface NSCalendar (NextWeekday)
- (NSInteger)maxWeekday;
- (NSDate *)dateFromComponents:(NSDateComponents *)comps
forNextWeekday:(NSInteger)weekday
atHour:(NSInteger)hour;
#end
#implementation NSCalendar (NextWeekday)
- (NSInteger)maxWeekday
{
return [self maximumRangeOfUnit:NSWeekdayCalendarUnit].length;
}
- (NSDate *)dateFromComponents:(NSDateComponents *)comps
forNextWeekday:(NSInteger)weekday
atHour:(NSInteger)hour
{
NSInteger diff = weekday - [comps weekday];
if( diff < 0 ){
diff += [self maxWeekday];
}
NSDateComponents * weekdayOffset = [NSDateComponents new];
[weekdayOffset setWeekday:diff];
[comps setHour:hour];
return [self dateByAddingComponents:weekdayOffset
toDate:[self dateFromComponents:comps]
options:0];
}
#end
#define GREGORIAN_THURSDAY 5
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSCalendar * cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * wednesday = [NSDateComponents new];
[wednesday setDay:3];
[wednesday setWeekday:4];
[wednesday setMonth:6];
[wednesday setYear:2013];
NSDateComponents * friday = [NSDateComponents new];
[friday setDay:5];
[friday setWeekday:6];
[friday setMonth:6];
[friday setYear:2013];
NSDateComponents * now = [cal components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit
fromDate:[NSDate date]];
NSDateComponents * lastSatOfDecember = [NSDateComponents new];
[lastSatOfDecember setDay:29];
[lastSatOfDecember setWeekday:7];
[lastSatOfDecember setMonth:12];
[lastSatOfDecember setYear:2012];
NSLog(#"From Wednesday: %#", [cal dateFromComponents:wednesday
forNextWeekday:GREGORIAN_THURSDAY
atHour:13]);
NSLog(#"From Friday: %#", [cal dateFromComponents:friday
forNextWeekday:GREGORIAN_THURSDAY
atHour:13]);
NSLog(#"From now: %#", [cal dateFromComponents:now
forNextWeekday:GREGORIAN_THURSDAY
atHour:13]);
NSLog(#"Crossing over the year: %#", [cal dateFromComponents:lastSatOfDecember
forNextWeekday:GREGORIAN_THURSDAY
atHour:13]);
}
return 0;
}
I am making an app that calculates salaries based on the years somebody is working for a firm. The user enter 2 dates, the first one is the day he started working (let's say 1/12/2010) and the second is the end date (let's say 1/02/2012). So far i know the total days he worked but i want to be more specific in order to calculate that he worked:
30 days in 2010 * 30$ per day = 90$
365 days in 2011 * 35$ per day = 12775$
32 days in 2012 * 40$ per day = 1280$
It's the first time i am working with dates and calendars and as you can guess i am totally unfamiliar with them.
Try this code :
-(NSInteger)daysBetweenTwoDates:(NSDate *)fromDateTime andDate:(NSDate*)toDateTime{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
return [difference day]+1;//+1 as if start and end both date are same, so 1 day worked.
}
- (IBAction)calculate:(id)sender {
NSDate *startDate=[self.startDate dateValue];//taking from datepicker
NSDate *endDate=[self.endDate dateValue];//taking from datepicker
NSTimeZone *gmt=[NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSDateFormatter *dateFormatterYYYY=[NSDateFormatter new];
[dateFormatterYYYY setTimeZone:gmt];
[dateFormatterYYYY setDateFormat:#"YYYY"];
NSDateFormatter *dateFormatterDDMMYYYY=[NSDateFormatter new];
[dateFormatterDDMMYYYY setDateFormat:#"dd/mm/YYYY"];
[dateFormatterDDMMYYYY setTimeZone:gmt];
//1. startDate to 31/12/StartDateYear
//2. loop to endDateYear
//3. 01/01/endDateYear to endDate
/* step 1 */
//find 31/12/StartDateYear
NSLog(#"YYYY ; %#",[dateFormatterYYYY stringFromDate:startDate]);
NSString *lastDateOfStartYearString=[NSString stringWithFormat:#"31/12/%#",[dateFormatterYYYY stringFromDate:startDate]];
NSDateFormatter *dateFormatddMMyyyy=[NSDateFormatter new];
[dateFormatddMMyyyy setDateFormat:#"dd'/'MM'/'yyyy"];
NSDate *lastDateOfStartYear=[dateFormatddMMyyyy dateFromString:lastDateOfStartYearString];
//no. of days
NSInteger year=[[dateFormatterYYYY stringFromDate:startDate]integerValue];
NSInteger days=[self daysBetweenTwoDates:startDate andDate:lastDateOfStartYear];
//add in dictionary
NSMutableDictionary *daysForYearDictionary=[NSMutableDictionary new];
[daysForYearDictionary setValue:#(days) forKey:[NSString stringWithFormat:#"%ld",year]];
/* step 2*/
NSInteger nextYearAfterStart=[[dateFormatterYYYY stringFromDate:startDate]integerValue]+1;
NSInteger previousYearBeforeEnd=[[dateFormatterYYYY stringFromDate:endDate]integerValue]-1;
for (NSInteger yearCounter=nextYearAfterStart; yearCounter<=previousYearBeforeEnd; yearCounter++) {
NSString *firstDateString=[NSString stringWithFormat:#"1/1/%ld",yearCounter];
NSDate *firstDate=[dateFormatddMMyyyy dateFromString:firstDateString];
NSString *lastDateString=[NSString stringWithFormat:#"31/12/%ld",yearCounter];
NSDate *lastDate=[dateFormatddMMyyyy dateFromString:lastDateString];
//no. of days
days=[self daysBetweenTwoDates:firstDate andDate:lastDate];
//add in dictionary
[daysForYearDictionary setValue:#(days) forKey:[NSString stringWithFormat:#"%ld",yearCounter]];
}
/* step 3 */
//find 1/1/EndDateYear
NSString *firstDateOfStartYearString=[NSString stringWithFormat:#"1/1/%#",[dateFormatterYYYY stringFromDate:endDate]];
NSDate *firstDateOfEndYear=[dateFormatddMMyyyy dateFromString:firstDateOfStartYearString];
//no. of days
year=[[dateFormatterYYYY stringFromDate:endDate]integerValue];
days=[self daysBetweenTwoDates:firstDateOfEndYear andDate:endDate];
//add in dictionary
[daysForYearDictionary setValue:#(days) forKey:[NSString stringWithFormat:#"%ld",year]];
//printing
for (NSString *yearStr in daysForYearDictionary) {
NSLog(#"Year : %#, Days Worked : %#",yearStr,[daysForYearDictionary valueForKey:yearStr]);
}
}
I'm a bit of a NSSortDescriptor n00b. I think, though, it is the right tool for what I need to do:
I have an NSArray consisting of objects with keys, say, "name" and "time". Instead of verbalizing it, here's an example:
input:
name: time
B: 4
C: 8
B: 5
C: 4
A: 3
C: 2
A: 1
A: 7
B: 6
desired output:
name: time
A: 1 <---
A: 3
A: 7
C: 2 <---
C: 4
C: 8
B: 4 <---
B: 5
B: 6
So the values are sorted by "time" and grouped by "name". A comes first because he had the smallest time value, and all values for A come after one another. Then comes C, he had the second smallest time value out of all his values. I have indicated the values that determine how the names are sorted; within each name group, sorting is by time.
How do I get from input to output NSArray in the most efficient way? (cpu- and memory-wise, not necessarily code-wise.) How would I construct the NSSortDescriptors for this, or use any other method? I don't want to roll my own unless it's the most efficient way.
My solution is:
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"time" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, sortDescriptor2, nil];
You can try it
The sortedArrayUsingDescriptors: NSArray method does most of what you need:
The first descriptor specifies the primary key path to be used in sorting the receiver’s contents. Any subsequent descriptors are used to further refine sorting of objects with duplicate values. See NSSortDescriptor for additional information.
Some filtering with NSPredicate is required too:
NSSortDescriptor *timeSD = [NSSortDescriptor sortDescriptorWithKey: #"time" ascending: YES];
NSMutableArray *sortedByTime = [UnsortedArray sortedArrayUsingDescriptors: timeSD];
NSMutableArray *sortedArray = [NSMutableArray arrayWithCapacity:[sortedByTime count]];
while([sortedByTime count])
{
id groupLead = [sortedByTime objectAtIndex:0];
NSPredicate *groupPredicate = [NSPredicate predicateWithFormat:#"name = %#", [groupLead name]];
NSArray *group = [sortedByTime filteredArrayUsingPredicate: groupPredicate];
[sortedArray addObjectsFromArray:group];
[sortedByTime removeObjectsInArray:group];
}
I have no idea if this is the most efficient method, but until you have reason to believe that it is causing problems there's no need to worry the performance implications. It's premature optimisation. I wouldn't have any concerns about the performance of this method. You've got to trust the framework otherwise you'll end up rewriting it (thus undermine the point of the framework) due to an unfounded paranoia.
I would create a new class called ItemGroup, and then add an extra ivar called group to your item class:
#interface ItemGroup : NSObject
{
NSNumber * time;
}
#property (nonatomic, copy) time;
#end
#interface ItemClass : NSobject
{
NSString * name;
NSNumber * time;
ItemGroup * group;
}
#property (nonatomic, copy) NSString * name;
#property (nonatomic, copy) NSNumber * time;
#property (nonatomic, assign) ItemClass * group; // note: must be assign
#end
Then, you could do the following:
NSMutableDictionary * groups = [NSMutableDictionary dictionaryWithCapacity:0];
for (ItemClass * item in sourceData)
{
ItemGroup * group = [groups objectForKey:item.name];
if (group == nil)
{
group = [[ItemGroup alloc] init];
[groups setObject:group forKey:item.name];
[group release];
group.time = item.time;
}
else if (item.time < group.time)
{
group.time = item.time;
}
item.group = group;
}
This code loops through the unsorted array, keeping track of the minimum time for each group, and also setting the group for each item. With that complete, you simply sort on group.time and time:
NSSortDescriptor * groupSorter;
groupSort = [NSSortDescriptor sortDescriptorWithKey:#"group.time" ascending:YES];
NSSortDescriptor * timeSorter;
timeSort = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES];
NSArray * sortDescriptors = [NSArray arrayWithObjects:groupSort, timeSort, nil];
NSArray * sorted = [sourceData sortedArrayUsingDescriptors:sortDescriptors];
And that should do the trick!
UPDATE: Note that you could get much better performance if you were able to assign the groups straight out of the gate. Something like this:
#interface ItemGroup : NSObject
{
NSString * name;
NSNumber * time;
}
#property (nonatomic, copy) NSString * name;
#property (nonatomic, copy) NSSNumber * time;
#end
#interface ItemClass : NSObject
{
ItemGroup * group;
NSNumber * time;
}
#property (nonatomic, retain) ItemGroup * group;
#property (nonatomic, copy) NSNumber * time;
#end
Now, if you maintain a list of groups somewhere (they could even go in an array somewhere, if need be):
ItemGroup * group_A = [[ItemGroup alloc] init];
group_A.name = #"A";
ItemGroup * group_B = [[ItemGroup alloc] init];
group_B.name = #"B";
...
And instead of setting the names of your data items, you set their group:
someItem.group = group_A;
someItem.time = GetSomeRandomTimeValue();
[sourceData addObject:someItem];
....
This would greatly simplify the loop used to set group times:
for (ItemClass * item in sourceData)
{
if (item.time < group.time) { group.time = item.time; }
}
And, if you really wanted to be blazing fast about it, you could even modify the property setter for your time property to set the group times on the fly:
#implementation ItemClass
- (void)setTime:(NSNumber *)newTime
{
if (newTime < group.time) { group.time = newTime; }
time = [newTime copy];
}
#end
Note that you would have to be sure that group had been set before you set the time. With this in place, you wouldn't need that sorting loop at all. The sortDescriptors would be enough.
I went through to make a little code (didn't try running it or really go over it so there might be a couple of mistakes, but it has the general idea) to do what you're looking for. Performance wise, it probably won't be the best if you start running into huge amounts of data. I'm sure there's a better way to do this, but I felt like doing it the most basic way as a "temporary fix" answer.
NSMutableArray *copiedarray = [YourFirstArray mutableCopy];
NSMutableArray *sortedarray = [[NSMutableArray alloc] init];
NSMutableArray *tempgroup = nil;
NSSortDescriptor * groupSorter = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:YES];
NSInteger i;
NSInteger savedlowest = -1;
NSString *savedname = #"";
while ([copiedarray count] > 0) {
///reset lowest time and group
savedlowest = -1;
savedname = #"";
///grab the lowest time and group name
for (ii = 0;ii < [copiedarray count]; ii++) {
if (savedlowest==-1 || ((YourClass *)([copiedarray objectAtIndex:ii])).time<savedlowest)) {
savedname = ((YourClass *)([copiedarray objectAtIndex:ii])).name;
savedlowest = ((YourClass *)([copiedarray objectAtIndex:ii])).time;
}
}
//we have the lowest time and the type so we grab all those items from the group
tempgroup = [[NSMutableArray alloc] init];
for (ii = [copiedarray count]-1;ii > -1; ii--) {
if ([((YourClass *)([copiedarray objectAtIndex:ii])).name isEqualToString:savedname]) {
///the item matches the saved group so we'll add it to our temporary array
[tempgroup addObject:[copiedarray objectAtIndex:ii]];
///remove it from the main copied array for "better performance"
[copiedarray removeObjectAtIndex:ii];
}
}
[tempgroup sortUsingDescriptors:[NSArray arrayWithObject:groupSorter]];
[sortedarray addObjectsFromArray:tempgroup];
[tempgroup release];
tempgroup = nil;
}
In the end you'll end up with what you're looking for in sortedarray.
You can use NSSortDescriptor. These descriptors are very useful as they let you do multiple key sort as well single key sorting. The case sensitivity and insensitivity is also easily achievable. I found a detailed example HERE
If you have to do more complicated sorting the just "ascending" can take care of (say sort NSString as if they were floats), you might want to do something like this:
NSDictionary *d = [self dictionaryFromURL:[NSURL URLWithString:urlStringValue]];
NSSortDescriptor *distanceSort = [[NSSortDescriptor alloc] initWithKey:#"distance" ascending:YES comparator:^(id left, id right) {
float v1 = [left floatValue];
float v2 = [right floatValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}];
NSSortDescriptor *nameSort = [NSSortDescriptor sortDescriptorWithKey:#"company_name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:distanceSort, nameSort, nil];
[distanceSort release];
NSArray *sortedObjects = [[d allValues] sortedArrayUsingDescriptors:sortDescriptors];
ILog();
return sortedObjects;