My data looks like:
objA date1 objnumber2
objB date2 objnumber1
objC date2 objnumber4
objD date2 objnumber3
objE date1 objnumber7
objF date3 objnumber6
objG date1 objnumber5
I am looking for all the objects which are from the Nth last date. The result objects need to be sorted using objnumber (this should be easy using NSSortDescriptor).
So if I specify N=1 (most recent date), I should get [objF] only. (date3 is most recent)
If N=3 (oldest date), I should get sorted [objA, objG, objF]
The sorting part is easy.
My question is do I really need to firstly search for the latest date (using combination of sortdescriptor and nsfetchrequest searchLimit) in the entire data? Then do a second search to find all objects from that date (using nspredicate) and sort it?
Or is there a better way to perform this type of search? How would you generalize this for Nth date instead of last date? That would be a big performance hit no??
NOTE that the date is not known beforehand.
Edit2: okay this is even more complicated since I am using NSDate. So pretty much all the objects have unique dates lol. Gotta throw nsdateformatter in there in the mix too :(
I figured out a solution to my problem.
Instead of using the date, I added a new Int64 NSNumber attribute. Everyday the obj is added, all the objects are tagged with that number for that particular day.
For retriving all objects from Nth day, I firstly do a "fetchlimit 1" nsfetchrequest for that number in a sorted fetchrequest. That gives me the last number.
Now for the Nth last objects, I subtract N from the last number. Then I perform a "nspredicted" nsfetchrequest for all objects with that number attribute. Then I simply just sort the result array.
This resolves my question :D
Related
I have an array of objects that has a member that is type Date, and I'm trying to sort the whole array by Date, and it's not sorting correctly.
This is the code I'm using, the name of the array is alarms and the name of the member type Date is time.
alarms.sort(by: { $0.time.compare($1.time) == .orderedAscending })
and whenever I sort it it just doesn't work correctly, and I'm testing it by printing all the values in a for loop.
Can someone help me with the syntax for this?
The compare is a NSDate function. With Date you can just use < operator. For example:
alarms.sort { $0.time < $1.time }
Having said that, compare should work, too, though. I suspect there's some deeper issue here, that perhaps your time values have different dates. You might only be looking at the time portion, but when comparing Date objects, it considers both the date and time. If you only want to look at the time portion, there are several ways of doing that, for example, look at the time interval between time and the start of the day:
let calendar = Calendar.current
alarms.sort {
let elapsed0 = $0.time.timeIntervalSince(calendar.startOfDay(for: $0.time))
let elapsed1 = $1.time.timeIntervalSince(calendar.startOfDay(for: $1.time))
return elapsed0 < elapsed1
}
There are lots of ways to do this, but hopefully this illustrates the idea.
I need to know how would I check if my given value is between two closest array's members. For example I have an array of dates with the date of week start in given period of time. And I need to check if my given date is in one of its week. For example:
2015-11-02
2015-11-09
2015-11-16
2015-11-23
And my given value is 2015-11-11 for example. How should I check if it is one of these weeks date? Thanks for help.
%w(2015-11-02 2015-11-09 2015-11-16 2015-11-23).any? do |date|
date.to_date.cweek == Date.today.cweek
end
And here is what this does:
First, you have an array of strings, you use any? to loop through it and check if any fulfils a requirement, then you cast you date strings into actual dates, and cweek gives you the number of a week in the year. Date.today gives you today's date.
Instead of Date.today.cweek you can use '2015-11-11'.to_date.cweek.
The loop above returns boolean; you could also get an array of values that fulfil a condition like this:
new_array = %w(2015-11-02 2015-11-09 2015-11-16 2015-11-23).map do |date|
date.to_date.cweek == '2015-11-11'.to_date.cweek
end.compact
Resources:
Date class on ruby-doc.org
Date & Time in Ruby on tutorialspoint.com
UPDATE
If you want to get from the database only records with a date from particular week, this is how you could do it:
my_date = '2015-11-11'.to_date
matching_records = MyResource.where( date: my_date.beginning_of_week..my_date.end_of_week )
The assumptions are that you have a model MyResource, and that it has a column date. What this does is returns a relation with all the records that have dates from the same week as my_date.
Assuming your dates array is sorted:
date >= dates.first && date <= dates.last
If you're dealing with strings, you can "require 'date'" and transform your strings to dates ("Date.parse('2001-02-03')").
As others have suggested, you can then see if your date is between the first and last entry of your list.
If the real list is sorted and each entry is one week apart, then you can easily find where in the list your guy is.
E.g., say the list is [date_0, date_1, date_2, ..., date_k] (all 1 week apart), and you're given a test_date between date_0 and date_k. Then (test_date.jd - date_0.jd)/7 gives you the index of the date in your list that is <= test_date.
I am using Core Data with an NSFetchedResultsController to display dates in a table view. Upon fetch I want these date objects sorted by the closest upcoming month/day to today while ignoring the year, preferably (if possible) without having to store the fetched results in an array and being forced to perform a separate sort operation on that array. That's the basic idea, the rest is context.
My NSManagedObject subclass Date has 2 attributes:
Date.date -> 1986-02-06 08:00:00 +0000 // the stored date of type Date
Date.equalizedDate -> 02/06 // computed from date ignoring year of type String
If I use NSSortDescriptor(key: "date", ascending: true) on the fetched results controller, then the dates in the table view are sorted by year (not what I want):
Date.date 1978-06-23 07:00:00 +0000 // 1978 = #1
Date.date 1986-02-06 08:00:00 +0000 // 1986 = #2
Date.date 1991-07-26 07:00:00 +0000 // 1991 = #3
If I use NSSortDescriptor(key: "equalizedDate", ascending: true) I can get one step closer by ignoring the year and showing proper ascension but it's not compared to today's date:
Date.date 1986-02-06 08:00:00 +0000 // 02/06 = #1 (February 6th)
Date.date 1978-06-23 07:00:00 +0000 // 06/23 = #2 (June 23rd)
Date.date 1991-07-26 07:00:00 +0000 // 07/26 = #3 (July 26th)
If, for example, today was April 5th, then I would want the fetched results to be fetched and displayed as follows:
// Today is April 5th
Date.date 1978-06-23 07:00:00 +0000 // 06/23 = #1 (June 23rd)
Date.date 1991-07-26 07:00:00 +0000 // 07/26 = #2 (July 26th)
Date.date 1986-02-06 08:00:00 +0000 // 02/06 = #3 (February 6th)
Currently, the only way for me to accomplish this is by storing the fetched results controller results in an array, performing a comparison upon the array, and using the array instead of my fetched results controller for the data source of my table view rows and cells:
// fetchedResults = [Date] <- stored Date objects from fetch
for fetchedDate in fetchedResults {
// formatDateIntoString changes 2015-10-26 05:43:22 +0000 into 10/26
if fetchedDate.equalizedDate < NSDate().formatDateIntoString() {
fetchedResults.removeAtIndex(0)
fetchedResults.append(fetchedDate)
} else {
break
}
}
return fetchedResults
I would prefer to omit this extra layer of logic because it not only eliminates the entire purpose of performance of a fetched results controller but it also gives me wonky troubles and wrong index paths when implementing swipe to delete (link for reference). I know that comparators and selectors don't work for fetched result controllers.
Is there any way to work around this issue?
If you do not care about sections, an easy approach is to keep your sorting for equalizedDate (if you have lots of dates, make sure you set that attribute to be indexed). You may also want to consider changing it to an integer rather than a string.
The array will be sorted from 01/01 to 12/31.
You can then, after the initial fetch, do another quick fetch to find the object closest to today's date.
That object now becomes your point of reference (or startingIndex).
In your FRC delegate, just use the starting index as the offset.
So, when asked to return the objet at index 0, you return a value based on the starting index.
something like...
NSUInteger actualIndex = (index + startingIndex) % count;
It's a little more complicated if you are using sections, but not much.
This way, you don't have to do anything special for sorting. It's also easy to change when the date changes in your app.
I think an easier solution is to simply modify your equalizedDate attribute as follows:
First, make it into a transient property.
Second, make it behave dynamically according to the current date. You can make this attribute very simple, but would then need to write more code to interpret it, or you can try to return something you can use directly.
A simple solution is to return just the offset from todays date (a number from 0 to 365). You would have to add functionality to return the correct date string (or date) based in this number and today's date.
An IMO better solution is to return a date with with a correctly normalized year. You would set the current and future dates to the current year, and all past dates to the next year, also resulting in 365 (366) dates.
You could now easily sort by the transient property, or even use it for sectioning.
I have times that I would like to compare to the current time for each day of the week. From what I've come across, it sounds like the best thing to do would be to have an array for each day of the week with the given times I want. For example:
mondayTimes(2:00:00, 5:00:00, 9:00:00, 14:00:00)
tuesdayTimes(3:00:00, 6:00:00, 10:00:00, 15:00:00)
etc...
I want to find out the given day of the week using the current date, and then depending on what day it is, use the array of times for that given day. Then use the current time to find which time is next in the array.
Basically it is like an "alarm clock" that always has set times for every day of the week.
Do I use NSStrings to populate the dates in each array and convert them so I am able to compare them to the current time? What is the best route to go about this?
Thanks!
To get the current date, use NSDate currentDate = [NSDate date];
Then, to extract the weekday: initialize a NSCalendar of your choice, then call [calendar components:NSWeekdayCalendarUnit fromDate:currentDate];, where calendar is your calendar instance.
This produces a number between 1 and 7 (for the Gregorian calendar) where 1 is Sunday and 7 is Saturday. I would then advise you put all of your times in an array of arrays, with the 1 index containing an array of your sundayTimes, 2 containing an array of mondayTimes, ... 7 containing an array of your saturdayTimes.
Then using [allTimes objectAtIndex weekday] will return a NSArray of your times. All that's left is to compare the times, which I'm sure you can figure out.
I've got an array of NSDates and I'd like to grab the largest NSDate from the array. While i could always sort them and grab the first/last, is there a way to do this with KeyValueCoding or some other quick one liner kind of way? I know that I could use something like valueForKeyPath#"#max.date" if the objects had a date property, but what if the objects are dates themselves??
thanks
You can use,
NSDate *maxDate = [dateArray valueForKeyPath:#"#max.self"];
This will give you the largest date from array. You dont have to sort the array before doing this.
From the documentation,
The #max operator compares the values of the property specified by the
key path to the right of the operator and returns the maximum value
found. The maximum value is determined using the compare: method of
the objects at the specified key path. The compared property objects
must support comparison with each other. If the value of the right
side of the key path is nil, it is ignored.
Note that #max will do compare: and then will find out the max value.
Agree with #SeanDanzeiser. To be more specific, here's a ~70 byte one-liner:
// if dateArray is the array of dates ...
NSDate *maxDate = [[dateArray sortedArrayUsingSelector:#selector(compare:)] lastObject];