I'm trying to come up with the best / cleanest way to do this.
Basically I have an array of NSDates, and I'm trying to get the UITableView Delegate methods (numberOfSectionsInTableView) to return the number of dates in the array that aren't the same.
So here's another example:
My array:
NSDate *1 (Monday 1st Jan)
NSDate *2 (Monday 1st Jan)
NSDate *3 (Tuesday 2nd Jan)
NSDate *4 (Wednesday 3rd Jan)
NSDate *5 (Wednesday 3rd Jan)
The array I would like:
NSDate *1 (Monday 1st Jan)
NSDate *2 (Tuesday 2nd Jan)
NSDate *3 (Wednesday 3rd Jan)
Probably the easiest solution is to pass you dates array in a set and then again into an array, but you loose sorting.
By definition NSSet can contain only one instance of equal instances.
NSSet *set = [NSSet setWithArray: datesArray];
By calling isEqual to one of each elements is guaranteed that you can't have duplicated dates. isEqual in a NSDate calls isEqualToDate that considers equality only if the instances represent the same date.
Then:
NSArray * newdates = set.allObjects
Is worth to mention that on NSArray you can also use KVC to make a distinct union:
NSArray * uniqueDates = [datesArray valueForKeyPath:#"#distinctUnionOfObjects.self"];
Related
I have an array with custom objects which have a NSDate property. I would like to somehow get all objects whose NSDate are on Sunday, Monday, Tuesday, and so on.
I feel like this isn't possible using predicates but hoping I am wrong and hoping there is a way without having to iterate over all objects, get the dates, convert them using date formatter and then figuring out the day.
I think block method of predicate code will be more feasible to do this task.
Here is my code
NSPredicate *pred = [NSPredicate predicateWithBlock:^BOOL(Object * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSCalendar* cal = [NSCalendar currentCalendar];
NSDateComponents* comp = [cal components:NSCalendarUnitWeekday fromDate:evaluatedObject.mDate];
NSInteger weekDay = [comp weekday]; // 1 = Sunday, 2 = Monday, etc.
return weekDay == 4;
}];
NSArray *arrFilteredObject = [arrData filteredArrayUsingPredicate:pred];
Here Object is my Custom object class which hold two field i.e one NSString and one NSDate attribute.
Here is my Object class for your reference
#interface Object : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSDate *mDate;
#end
Hope it helps. Please let me know if you face any issue with this approach.
I was able to figure this out. Here's my way in Objective-C which can also be adapted for swift easily I am sure.
I actually have an array of custom objects each of which has a NSDate property which I need to be filtering on by day of week.
I achieved my solution by adding another custom getter to my custom object:
Interface:
#property (nonatomic, retain, getter = dayOfWeek) NSString *dayOfWeek;
Implementation:
-(NSString*)dayOfWeek{
return [[(AppDelegate*)[[UIApplication sharedApplication] delegate] dayOfWeekFormatter] stringFromDate:self.createdAt];
}
The dayOfWeekFormatter is a NSDateFormatter which I create in my AppDelegate which can be reused instead of recreating it each time:
#property (strong, nonatomic) NSDateFormatter *dayOfWeekFormatter;
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
self.dayOfWeekFormatter = [NSDateFormatter new];
[self.dayOfWeekFormatter setDateFormat:#"eeee"];
[self.dayOfWeekFormatter setLocale:locale];
You must set the Locale!
Now I am able to use this predicate to filter for whatever day I need. Here's an example for filtering for all objects on Wednesday:
NSArray *dayArray = [myTestArrayOfObjects filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"dayOfWeek == 'Wednesday'"]];
i have date string in form of "yyyy-MM-dd HH:mm" i have some 100 obhjects in an array with different date and time.now my question is how to sort this array based on time and date, i tried in many ways but no use .can any one help me .thanks in advance
used this method for sort an array.
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO];
NSArray *sortedArray = [detailsArray sortArrayUsingDescriptors:#[sortDescriptor]];
May be help for you
If you have an array of strings, take advantage of your date format and just sort it lexically.
NSArray* sorted = [dates sortedArrayUsingComparator:^(NSString* a, NSString* b) {
return [a compare:b];
}];
If you actually have an array of NSDate objects, use the NSDate compare functions, using almost identical code (compare: works to compare two homogenous data types in many cases)
NSArray* sorted = [dates sortedArrayUsingComparator:(NSDate* a, NSDate* b) {
return [a compare:b];
}];
With the simplicity of the comparator, you can actually just use the selector directly:
NSArray* sorted = [dates sortedArrayUsingSelector:#selector(compare:)];
Currently I get all the entities of this type and search for max value via for-each cycle.
I use NSFetchRequest with NSPredicate as param.
Is it possible to find this entity via one core data request only? As I understand this request should contain a conversion to a date and date comparison.
Yes, you can, but you'll need a trick somewhere.
If the string format would be like yyyy/MM/dd HH:mm:ss you could do this easily with a sort descriptor. Since you are using the format MM/dd/yyyy this will not work, since it will sort with months first.
However, you can create a category on your managed object which adds an NSDate property, which dynamically converts your string dates into real dates. In your sort descriptor you can then use this category field, sort descending and pick the first result.
EDIT: NSSortDescriptors cannot be used with transient properties, so this is not an option. (See also Martin R's comment below).
Another option would be to add an extra date field to your entity, which stores the same date in real date-format. Therefore you would need to update your current database model, but this would probably perform slightly better.
To clarify the last option:
(Assuming you have added a date field called: 'realDateTime')
When storing the entities you can add the following code:
// String to date conversion
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM/dd/yyyy HH:mm:ss"];
realDateTime = [dateFormatter dateFromString:dateTime];
To find the entity with the most recent date, you can implement a method which looks something like this:
- (NSManagedObject *)findObjectMostRecentDate {
// Set a sort descriptor to set last date as first result
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:#"realDateTime" ascending:NO]]];
// Execute the fetch request
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:nil];
if (results.count) {
return [results firstObject]; // <-- This returns the object with the 'max' date
else
return nil; // No objects fetched
}
In my application I have an array of strings representing the dates in the Format MM/dd/YYYY
and below is the code I used to sort this array
NSDateFormatter *formatter =[[NSDateFormatter alloc]init];
[formatter setDateFormat:#"MM/dd/YYYY"];
NSLog(#" arr %#",arr);
[arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
NSDate *date1=[formatter dateFromString:obj1];
NSDate *date2=[formatter dateFromString:obj2];
return [date1 compare:date2];
}];
NSLog(#" arr %#",arr);
below is the output of nslog
2013-04-08 17:23:48.112 SEEMR[2792:c07] arr (
"02/18/2013",
"02/16/2013",
"02/14/2013",
"01/16/2013",
"02/13/2013",
"03/16/2013"
)
2013-04-08 17:24:07.662 SEEMR[2792:c07] arr (
"02/18/2013",
"02/16/2013",
"02/14/2013",
"01/16/2013",
"02/13/2013",
"03/16/2013"
)
But it is not sorting as expected so help me peers
When using NSDateFormatter, always test if your formatting string is correct. Yours isn't. It should be MM/dd/yyyy (the case is important).
In other words, since your formatter works incorrectly, all dateFromString: return nil and you are always comparing nil with a nil, which returns 0.
Saving your dates as a NSDate instances would make your life easier. In general, it's better to convert the date into NSString only if you are presenting it to the user or sending it to some other application (e.g. web service).
You can do something like this:
NSSortDescriptor* sortByDate = [NSSortDescriptor sortDescriptorWithKey:#"date property name" ascending:YES];
[mutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortByDate]];
Hope it helps!
I've got an issue and I'm wondering if it's possible to solve. I have a UITableView that uses Core Data and NSFetchedResultsController. A big part of my app is organizing things based on dates. Ideally, I'd like to divide the TableView into sections based off of 3 date ranges. These ranges would be between now and 2 days, between 2 days and 6 days, and 10 days and beyond. The user has a UIDatePicker, and when they enter a date it would be automatically put into one of these organized sections. Now I know how to easily divide the tableview into sections by each single date, but not how to do it so each section has a time range. Thanks to anyone that might be able to help out.
Just did this myself. The end result is that core data objects are sorted into sections, each one being 1 day wide. Multiple objects may be in one section.
//at a point where you define your fetched results controller
//add #"sectionTitle" as sectionNameKeyPath:
//...
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:#"sectionTitle" cacheName:#"CacheName"];
//....
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString* dateString = [[[self.sleepQualityController sections] objectAtIndex:section] name];
return dateString;
}
//add this to your managed object's header file:
#property(nonatomic,assign)NSString* sectionTitle;
//this goes into your managed object's implementation file
-(NSDate *)dateWithOutTime:(NSDate *)datDate
{
if( datDate == nil ) {
datDate = [NSDate date];
}
NSDateComponents* comps = [[NSCalendar currentCalendar] components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:datDate];
return [[NSCalendar currentCalendar] dateFromComponents:comps];
}
-(NSString*)sectionTitle
{
NSDateFormatter* dateFormaterWeekdayMonthDay = [[NSDateFormatter alloc] init];
dateFormaterWeekdayMonthDay.dateStyle = NSDateFormatterLongStyle;
return [NSString stringWithFormat:#"%#", [dateFormaterWeekdayMonthDay stringFromDate:[self dateWithOutTime:self.date]] ];
}
I would explore using NSExpressions in the fetch request. It can be difficult to find good documentation on using SQL-like expressions with fetch requests; but you can also write your own block for the query to use, which is pretty killer.
Basically what you want is an NSExpression which returns a string for the section name, which you can tell the NSFetchedResultsController to use for the section name key path. You will need to build up an NSExpressionDescription around the expression to add it to the fetch request.
I hope this puts you in the right direction!
FWIW, I would normally be inclined to do an SQL-ish solution (essentially selecting a 'case' expression which compares the date field and selects one of three values), but sometimes with Core Data, it's easier to just pull out the sledge hammer (or rather, it feels like that's what they want us to do).