I have an NSMutableArray with the latest 30 days records fetched from a DB.
Each record consists of a string and a creation date.
Now I want those records to feed an UITableView that will have a section for each different day and one cell per section simply showing how many records exist for each day.
Well, it's been hours of thinking and I'm not able yet to figure out how to do it. All the questions I've found on stackoverflow are related to core data, but I don't use it in my app.
Any help will be appreciated.
//after fetching data,write below code<br>
int cArray[30]; //create it as global array.cArray stores number of records that fall in particular date. if cArray[j]=10, it means for j'th date, there are 10 records.
// initialise all to zero.
for (int i=0; i<30; i++) {
cArray[i]=0;
}
NSArray *myArray;// my array stores your records.
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Record *rec = (Record*)obj;
cArray[(int)(rec.creationDate)]++;
}];
And later you can use cArray to show number of records that fall in a particular date.
Hope this helps.
I have implemented my data model similar to this case. I have an array of the NSDateComponents for each day and have a dictionary which keys is the date components that is stored in the array and values is the arrays that is store the data for each day. This approach is working pretty well for me.
You can group your data according to date. Then you can store grouped array on a dictionary and the key will be the date and this key will be your table header and the value will be the list of data at that date. I hope you got this approach.
Related
Given a table of books published, with a date_published column of typeNSAttributeType.DateAttributeType, I would like to know how many books were published by year like this:
Year | Books
-----+------
2013 | 76
2014 | 172
2015 | 155
In plain old SQL this is simple (although it varies slightly by RDBMS):
SELECT DATEPART(yyyy, date_published) AS "Year", COUNT(*) AS "Books"
FROM books
GROUP BY DATEPART(yyyy, date_published)
I'm new to Swift and iOS in general but everything I looked at suggested either pre-computing the year and storing that, or loading all the data and counting it myself. Neither of these approaches suited me as the year is in fact an accounting year (that can vary after storage) and the amount of data is potentially large.
Most approaches revolved around adding a custom attribute to my NSManagedObject. That seems like it's too late to me because the object would not have been loaded into memory at this stage. There were also discussions around NSFetchedResultsController with sectionNameKeyPath's, but again this feels like it's too late in the fetch process. I found NSExpression convoluted so I may well have missed something but it seems like I can't invoke a custom Swift function here. Really, at the end of the day, I expected to find built-in functions for things like DATEPART, DATEADD, DATEDIFF, and I was hoping somebody could point me in the right direction.
As a more concrete example consider the UK tax year which runs 6 April to 5 April. To calculate the tax year I would subtract 3 months and 6 days (to midnight on 5 April). So for a book published on 1 March 2012 I would do the subtraction which would give me 24 November 2011, including 29 days in February for the leap year. From this I simply extract the year part, 2011. So the UK tax year for 1 March 2012 is 2011. I could precompute 2011 and store that in a new column. But then if I move from the UK to Australia the fiscal year changes to July through June. More likely I have a company with a different accounting period than the fiscal year (very likely in the UK). That company then gets taken over by a US group that uses the calendar year, and everyone is happy, except my little app that thinks March 2012 is in 2011.
Here's some boilerplate to get going... with no attempt to group by year:
// The raw date for grouping by - no attempt to extract year
let date = NSExpressionDescription()
date.expression = NSExpression(format: "date_published")
date.name = "date"
date.expressionResultType = .DateAttributeType
// The number of books
let books = NSExpressionDescription()
books.expression = NSExpression(format: "count:(publication_title)")
books.name = "books"
books.expressionResultType = .Integer32AttributeType
// Put a fetch together
let fetch = NSFetchRequest(entityName:"Book")
fetch.resultType = .DictionaryResultType
fetch.propertiesToFetch = [date, books]
fetch.propertiesToGroupBy = [date]
// Execute now
var error: NSError?
if let results = context.executeFetchRequest(fetch,
error: &error) as Array<NSDictionary>? {
for row in results {
let date = row.valueForKey("date") as? NSDate
let books = row.valueForKey("books") as? Int
NSLog("%# %d", date!, books!)
}
} else {
NSLog("Fail!")
}
Thanks for any pointers!
As you're finding, this touches on something that's a weak spot in Core Data's API. It's common to explain that one shouldn't think of Core Data in terms of SQL because it uses a different approach. Dates are where this can be really annoying, because Core Data hides some SQLite capabilities. (They do this at least partly because Core Data isn't a SQLite wrapper, and can work with other, non-SQL storage systems).
The core problem is that Core Data's "Date" type corresponds to an NSDate, and NSDate in turn is just a floating-point number representing the number of seconds since a reference date. It doesn't include year, month, or day. Those values are not even fixed, because the instant in time represented by an NSDate might mean a different date in California as opposed to Japan, for example. The word "date" in these type names is unfortunately misleading.
This is why people generally recommend using extra fields, or at least different data types, for apps using Core Data that need to consider the actual date in some time zone as opposed to a precise moment in time regardless of zone. There isn't a good way to construct a Core Data query that operates on a "Date" field that does what you need. Dealing with this comes down to storing the data you actually need instead of something that just approximates what you need-- except that calling this type "Date" confuses the choice. You don't want a Core Data "Date" type here.
So let's consider one approach to getting the result you need while making SQLite do as much of the work as possible. Let's suppose you replace your date field with an integerDate field that represents the date as an integer (Core Data "Integer 64") using the format yyyyMMDD. Today would be stored as 20151223. In theory this could be done in one step with some NSExpression wizardry, but Core Data doesn't let you group by expressions, so that's out.
Step 1: Get all distinct year values
NSExpression *yearExpression = [NSExpression expressionWithFormat:#"divide:by:(%K,10000)", #"integerDate"];
NSExpressionDescription *yearExpDescription = [[NSExpressionDescription alloc] init];
yearExpDescription.name = #"year";
yearExpDescription.expression = yearExpression;
yearExpDescription.expressionResultType = NSInteger64AttributeType;
NSFetchRequest *distinctYearsRequest = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
distinctYearsRequest.resultType = NSDictionaryResultType;
distinctYearsRequest.returnsDistinctResults = YES;
distinctYearsRequest.propertiesToFetch = #[ yearExpDescription ];
NSError *fetchError = nil;
NSArray *distinctYearsResult = [self.managedObjectContext executeFetchRequest:distinctYearsRequest error:&fetchError];
if (distinctYearsResult != nil) {
NSLog(#"Results by year: %#", distinctYearsResult);
}
NSArray *distinctYears = [distinctYearsResult valueForKey:#"year"];
In the above, yearExpression gets the year portion of integerDate by simple division. When the above completes, distinctYears contains all the years represented by integerDate.
Step 2: Loop through years, getting a count for each:
NSMutableDictionary *countByYear = [NSMutableDictionary dictionary];
for (NSNumber *year in distinctYears) {
NSFetchRequest *countForYearFetch = [NSFetchRequest fetchRequestWithEntityName:#"Event"];
countForYearFetch.resultType = NSDictionaryResultType;
countForYearFetch.propertiesToFetch = #[ yearExpDescription ];
NSExpression *targetYearExpression = [NSExpression expressionForConstantValue:year];
NSPredicate *yearPredicate = [NSComparisonPredicate predicateWithLeftExpression:yearExpression rightExpression:targetYearExpression modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
countForYearFetch.predicate = yearPredicate;
NSError *fetchError = nil;
NSUInteger countForYear = [self.managedObjectContext countForFetchRequest:countForYearFetch error:&fetchError];
countByYear[year] = #(countForYear);
}
NSLog(#"Results by year: %#", countByYear);
This does a separate fetch for each year, but keeps the memory overhead low by only fetching the count of results instead of the actual data. When this finishes, countByYear has the number of entries by year, based on the integerDate field.
Having said all this, keep in mind that you do have the option of using SQLite directly instead of using Core Data. PLDatabase will give you an Objective-C style wrapper while still allowing raw SQL queries for everything SQLite can do.
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
my app is in the appstore and after updating to ios 8 and testing the app on my new iphone 6 the app doesnt work correctly.
ive already found the problem: i stored 3 dimensional arrays in nsuserdefaults and its giving me the error:mutating object sent to immutable....
so i can forget to store 3d arrays in nsuserdefaults, because i can`t really change those.
My idea was to store all the data in core data. GREAT!
But i gotta learn!
So....
I have got 5 attributes like you can see on the image:
when pushing an add-button all of the 5 attributes get saved by core data in the sqlite file.
The attribute "datum" of type date comes from a datepicker,so i can select a date. the other attributes are hours,minutes and some note - strings.
so, i want to look my tableview like this:
2014 <--- sectiontitle with year
-October <--- rows cell.text
-November <--- rows cell.text
2015 <--- sectiontitle with year
-January <--- rows cell.text
-May <--- rows cell.text
In this example i made entries for october+november in 2014 and january+may in 2015.
This means, this tableview is an overview to all the data,that i want to be filtered in the year and month.
what is the best approach to do this.
is there an easier way than: fetching the date, putting the dates into an array, setting dateformatter and find non-equal dates to get the years and the months?
because, when pushing a cell, for example october in section 0, a detailview opens and i want to display all entries i made in october in 2014.
What is your advice? Please help!!!
If user defaults is sufficient for your needs, I'd stick with it but convert the data to something mutable when you read it. You can convert an immutable property list to a mutable one using CFPropertyListCreateDeepCopy. As the name implies, it's a deep copy, and you can have it convert mutable containers and/or leaves to mutable equivalents.
If you read from user defaults into NSDictionary *userDefaultsData, convert with
NSDictionary *mutableData = CFPropertyListCreateDeepCopy(NULL, userDefaultsData, kCFPropertyListMutableContainers);
That will recursively copy the entire thing, converting NSDictionary to NSMutableDictionary and NSArray to NSMutableArray as it goes.
Thanks again Tom, you saved me from much work!!!
In appdelegate i changed...
stdPickerArray= [[NSMutableArray alloc] initWithArray:[def objectForKey:#"gesamtStdArray"]];
to...
stdPickerArray= [[NSMutableArray alloc] initWithArray:[def objectForKey:#"gesamtStdArray"]];
stdPickerArray = CFBridgingRelease(CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)stdPickerArray, kCFPropertyListMutableContainers));
awesome!!!
I have a sqlite database as columns groupno, sunday, monday, tuesday, wednesday, thursday, friday and saturday.
I have retrieved the data using the following code. But this code gives all data in a single column (for e.g. code skips all previous values in a column and displays final item at the end of the column). However what I need is data from a specific row.
I do not know how to access a single item from NSArray. I would be really grateful for any help.
Thanks.
NSArray *info = [dba getinfo];
for(Getinfo *p in info)
getinfo.text = [p sunday ];
Thanks everyone,
I was able to solve the problem by following code to select the specific item,
id schedule = [info objectAtIndex:0];
_displayinfo.text = [schedule sunday];
Thanks again
Now I have a 1-dimonsion NSArray, each element of which is a NSMangedObject with a NSDate Attribute,
for example
an event Entity has an date(NSDate) attribute and name(NSString) attribute.
So the initilized array should be like this:
(NSArray) Events: event1, event2, event3,...,nil
Now, I need to group these objects by their date so that I can make this array the data source of a sectioned tableView.
And I just need the elememts in a same section have the same date, here the date means year,month,day, and with no hour or minute.
So the grouped NSArray should be a 2-dimension array, such as:
(NSArray) Events: day1, day2, day3, day4,...,nil;
and here day1,day2... are also a NSArray, and inside of them are really event objects. like this:
(NSArray) day1: event1, event2, event3, ..., nil;
So how can I realize this ?
I guess I can not use NSFetchedResultsController, since actually I have to fetch several entities, each of which has a NSDate attribute, and put them together to be grouped by date.
and I believe there's no problems about fetching,
I jus wonder how can I transfer the fetched 1-dimension array into the date-based 2-dimension array that I need?
Hoping someone to help and thanks very very much for answering!
You can use a custom NSValueTransformer to convert your entities' dates to ranges (or days). From there, you can use bindings with that if you like.