Ordering objects under sections - uitableview

Below is a screenshot of a UITableView that I want to order into by month. Currently their ordered by the first letter of the sub-heading in alphabetical order, what code should I use to make the events ordered into months? (By the way I have worked out how to order the sections)
Any help appreciated,
Seb

The best solution depends on what your data model looks like. Probably the simplest and most efficient, assuming your data model does not change very frequently, is just to have a routine that sorts each section's data in the order you want (based on each item's date). Create a list of pointers (or indices, again depending on your data structure's details), then all you have to do is look up the row index in the section's sorted index structure and display that element's data in your cellForRowAtIndexPath.
To optimize, you can keep a boolean "sorted" field in your data structure that is set to false whenever the data mutates in the table, then only sort on demand in the cellForRowAtIndexPath and set "sorted" to true.
EDIT: request for more step-by-step detailed description
OK, here's a bit more detail on how I'd go about it, assuming each of your sections is stored unsorted in a sortable container like NSMutableArray. Again the best solution depends on the details of your app like how frequently the section entries update and how you organize your data, and is there an upper bound on the number of entries in a section, etc. This is slightly different from my original suggestion in that the section's data container is directly sorted, and doesn't use an external ordering index.
Add a NSMutableSet sortedSections member somewhere convenient, close to your data model (inside its class would be best if you define it in a class)
// a section number is sorted if and only if its number is in the set
NSMutableSet *sortedSections;
When entries in sections are changed, added, or deleted, mark that section as unsorted by removing its number from the set
// just added, deleted, or changed a section entry entry
unsigned int sectionNum; // this section changed
...
NSNumber *nsNum = [NSNumber numberWithUnsignedInt:sectionNum];
[obj.sortedSections removeObject:nsNum];
In your cellForRowAtIndexPath (this may not be the most optimal place for this check, but it is a fast check and is the easiest location to get the sorting working), check if the section is sorted.
unsigned int sectionNum = [indexPath section];
NSNumber *nsNum = [NSNumber numberWithUnsignedInt:sectionNum];
if ( [obj.sortedSections containsObject:nsNum] )
// already sorted, nothing to do
else
{
// section needs to be resorted and reloaded
[mySectionData sortUsingFunction:compareSectionEntriesByDate context:nil];
// mark the section as sorted now
[obj.sortedSections addObject:nsNum];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationNone];
}
Here is an example sorting function, assuming your entry structures are of type NSDictionary and that you store the dates as NSDate objects with the key constant kEntryNSDate (which you would define yourself)
// sort compare function for two section entries based on date
//
static int compareSectionEntriesByDate( id e1, id e2, void *context)
{
NSDictionary *eDict1 = (NSDictionary *) e1;
NSDictionary *eDict2 = (NSDictionary *) e2;
NSDate *date1 = [eDict1 objectForKey:kEntryNSDate];
NSDate *date2 = [eDict2 objectForKey:kEntryNSDate];
int rv = [date1 compare:date2];
return rv;
}
That should be enough detail to get you going, good luck!

Related

searching large arrays in Objective-C

I have two very large NSMutableArray of strings containing more than 40k records each. I have to take each element from one array and sort that string into another array then make a new array which conatins only those records that are in both array. I have implemented the following code which take too much time as well as a lot of memory space also (crash in device). Are there any ways to solve this problem in a more efficient manner.
// _perArray and listArray contains more then 30K records each
for(NSString *gak in _perArray){
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF LIKE[c] %#",gak];
NSArray *results = [listArray filteredArrayUsingPredicate:predicate];
if(results.count>0){
[_resultArray addObject:results[0]];
}
}
Use binary search
index sort one array (the one with less records)
this will enable the usage of binary searching
sort the lesser array just to need less memory for index array
loop through the second array
for each record binary search in the first array
if found add record to output array
do not forget to preallocate the output array to avoid reallocation slowdowns
What it means:
Let N,M be the array sizes where N<=M
naive approach is O(N.M)
this approach (depending on used sort) leads to O(N.log(N).log(M))
Sort both arrays and use single pass incremental search
the complexity will lead to something like O((N.log(N))+(M.log(M))+M)
which in therms of complexity turns to O(M.log(M))
So:
index sort booth arrays
loop through M
increment index for array with lesser record
if match found add it to output array
To be more specific bullet 2 will be something like this (if arrays are sorted ascending):
// variables
string m[M],n[N],o[N]; // your arrays any string type with overloaded <,== operators
int M,N,O; // arrays sizes
int ixm[M],ixn[N]; // indexes for index sort
int i,j;
// bullet 2
for (i=0,j=0,O=0;;)
{
if (m[ixm[i]]==n[ixn[j]]) { o[O]=m[ixm[i]]; O++; }
if (m[ixm[i]]< n[ixn[j]]) { if (i<M) i++; else { if (j<N) j++; else break; }}
else { if (j<N) j++; else { if (i<M) i++; else break; }}
}
If you encode the string comparisons right you can do booth if conditions with single comparison
[notes]
if you do not want to use any of these approaches then there is also another way
you can add flag to one array telling you if it is already used
if it is skip the use of it during your comparisons
that will speed up your naive approach about 2 times
from M.N string comparisons you will need to do just M.N/2
If you have too big data chunks to fit in memory
then segmentate both arrays to some size fit to memory/Cache/...
and first index sort all segments
then do one of the above approach on all segments combinations
the only thing you need to add is checking if O[] does not already contain added string
if you arrays does not have multiples of the same string then this is not the case
otherwise keep O[] sorted or index sorted
and check by binary search ...
this segmentation will be speeded up with the used flag significantly
Always balance the performance issues with the frequency this code path is called. Going the database route might introduce a whole new set of issues to deal with while simply performing the sort in the background, and cutting down the size of the array first might be good enough.
Remove the duplicates first using NSMutableSet
Add all the objects, NSString in this case, to an NSMutableSet. This will eliminate the duplicates. Then sort the remaining objects.
NSArray *array1;
NSArray *array2;
NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithArray:array1];
[mutableSet addObjectsFromArray:array2];
NSSortDescriptor *sortDescriptor = nil; // You'll need to create a sort descriptor.
NSArray *result = [mutableSet sortedArrayUsingDescriptors:#[sortDescriptor]];
// Alternative
NSArray *result = [[mutableSet allObjects] sortedArrayUsingSelector:#selector(compare:)];
I wrote a quick Obj-C test you can try at the command-line.
Run it in the background and return when finished.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
// Perform the sorting
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Tell the main thread I'm done.
});
});

iOS Select specific entries to display in table

I have an array, which contains multiple dictionaries.
Formatted like this:-
Array
- Index
- DOW = Weekday
- Index
- DOW = Weekend
- Index
- DOW = Weekday
I want to populate a table with data from only those dictionaries containing DOW = Weekday.
What is the quickest way of enumerating this?
Note that the data will be changed via filters, and then table reloaded.
You can filter your array using NSPredicate, like this:
NSArray *data = #[
#{#"Dow":#"Weekday", #"One":#"Two"} // Item 0
, #{#"Dow":#"Weekend", #"Three":#"Four"} // Item 1
, #{#"Dow":#"Weekday", #"Five":#"Six"} // Item 2
];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(Dow=='Weekday')"];
NSArray *res = [data filteredArrayUsingPredicate:pred];
The code above will keep NSArrays items 0 and 2, and remove item 1, because it does not contain an entry where #"Dow" is set to #"Weekday".
The data variable above represents the pre-filtered data in your example.
I'd suggest you read Apple UITableViewDataSource protocol guide here and perhaps this tutorial too.
Basically you'll want to -
Override numberOfSectionsInTableView: and return the number of sections in your table (0 if it's not relevant).
Ovveride tableView:numberOfRowsInSection: and here you can "apply" the needed filters on your dictionary and return the correct number of rows you actually want to display.
Next time you update the filters and call reloadTable again the above steps will filter the table correctly according to the new filters.

Find the average of an NSMutableArray with valueForKeyPath

Currently I'm trying to find a compact way to average a matrix. The obvious solution is to sum the matrix, then divide by the number of elements. I have, however, come across a method on the apple developer website that claims this can be done in a simpler way using valueForKeyPath. This is linked here:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/CollectionOperators.html
Here is the example I'm currently working on to try and get it to work:
-(void)arrayAverager
{
NSMutableArray *myArray = [NSMutableArray arrayWithCapacity:25];
[myArray addObject:[NSNumber numberWithInteger:myValue]];
NSNumber *averageValue = [myArray valueForKeyPath:#"#avg.self"];
NSLog(#"avg = %#", averageValue);
}
The problem is: instead of averaging the array it merely prints out the elements in the array 1 by 1.
UPDATE
-(void) pixelAverager
{
//Put x-coordinate value of all wanted pixels into an array
NSMutableArray *xCoordinateArray = [NSMutableArray arrayWithCapacity:25];
[xCoordinateArray addObject:[NSNumber numberWithInteger:xCoordinate]];
NSLog(#"avg = %#", [xCoordinateArray valueForKeyPath:#"#avg.intValue"]);
}
You need to use #avg.floatValue (or #avg.doubleValue, or what have you). The #avg operator will average the property of the objects in the array specified by the name after the dot. The documentation is confusing on this point, but that is what:
to get the values specified by the property specified by the key path
to the right of the operator
Is saying. Since you have a collection of NSNumber objects, you use one of the *value accessors, e.g. floatValue to get the value of each object. As an example:
#include <Foundation/Foundation.h>
int main(void) {
NSMutableArray *ma = [NSMutableArray array];
[ma addObject:[NSNumber numberWithFloat:1.0]];
[ma addObject:[NSNumber numberWithFloat:2.0]];
[ma addObject:[NSNumber numberWithFloat:3.0]];
NSLog(#"avg = %#", [ma valueForKeyPath:#"#avg.floatValue"]);
return 0;
}
Compiling and running this code returns:
$ clang avg.m -framework Foundation -o avg
steve:~/code/tmp
$ ./avg
2013-01-18 12:33:15.500 avg[32190:707] avg = 2
steve:~/code/tmp
The nice thing about this approach is that this work for any collection, homogenous or otherwise, as long as all objects respond to the specified method, #avg will work.
EDIT
As pointed in the comments, the OP's problem is that he is averaging a collection with one element, and thus it appears to simply print the contents of the collection. For a collection of NSNumber objects, #avg.self works just fine.
No, you can't do like this. The object Transaction is a Modal Class. This class is having three properties, namely
payee
amount
date
Each row in this image represents one Transaction modal object.
transactions is an array which is holding all these rows (Transaction Modal Objects).
In these transactions array, they are trying to calculate the Transaction Modal amount field average using the operator #avg. So, its like
NSNumber *transactionAverage = [transactions valueForKeyPath:#"#avg.amount"];
your array doesn't have the key self. So that's the problem

NSMutableArray: How add a sorting value (int) to array's objects AFTER sorting it?

Got a tableView with some player objects in it, sorted ASC by best playerTime after each game try a player makes.
Now I want to add a value (int) that shows the players placement in the playerlist (tableview), that will be reflected on the the players custom cell in the tableView. Got the custom cell working already with the dynamic data, just I need the "placement" int.
How do I handle the array to do this? Must be done after the list (NSArray) been sorted by best playerTime.
I guess, some kind of iteration step one and two maybe (like an if-then statement)?
Maybe there is a smart NSArray property for this specific need?
Any tips or suggestions? Thank's.
Just display the row's value into your custom cell's label:
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//...
myCustomCell.positionLabel.text = [NSString stringWithFormat:#"%i", (indexPath.row + 1)];
//...
}
Now if you have a player object and want to retrieve its position in your sorted array, you just do this:
int playerPosition = [mySortedArray indexOfObject:playerObject] + 1;

Selecting Multiple Dates in Calendar

I am following This Blog to add a calendar component in my application. Now I want to select more than one dates to show some report for selected dates.
How can I do that ?
Thanks
For this purpose in this tutorial tappedTile method is use for selection of date.so for selecting a number of dates you need to make array and add all the strings.
use like this
NSMutableArray *eventArray//your array for adding dates,make it propeerty and alloc it.
- (void)calendarView:(KLCalendarView *)calendarView tappedTile:(KLTile *)aTile{
int month;
month=[aTile.date monthOfYear];
int day;
day=[aTile.date dayOfMonth];
int year=[aTile.date yearOfCommonEra];
NSString *dateForCompare;
dateForCompare=[NSString stringWithFormat:#"%i/%i/%i",month,day,year];
[eventArray addObject:dateForCompare];
//use this array (having dates in string format).
// use this by using your logical capability
}
see this link,may be it helps you

Resources