Extracting specific data from a Mutable Dictionary - iphone

I have a Mutable Dictionary which allows people to choose a selection of days of the week. Once a day has been selected the state is updated using numberWithBool.
When I NSLog the output it looks something like this:
{
day = Monday;
isSelected = 1;
},
{
day = Tuesday;
isSelected = 0;
},
{
day = Wednesday;
isSelected = 0;
},
{
day = Thursday;
isSelected = 0;
},
{
day = Friday;
isSelected = 0;
},
{
day = Saturday;
isSelected = 0;
},
{
day = Sunday;
isSelected = 1;
}
I would like to be able to extract the chosen days and produce the output in the form of a string. So in this example the output would be: Monday, Sunday
How can I do this?
My code for creating the dictionary is below:
NSMutableArray * tempSource = [[NSMutableArray alloc] init];
NSArray *daysOfWeek = [NSArray arrayWithObjects:#"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday", #"Sunday",nil];
for (int i = 0; i < 7; i++)
{
NSString *dayOfWeek = [daysOfWeek objectAtIndex:i];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:dayOfWeek, #"day", [NSNumber numberWithBool:NO], #"isSelected",nil];
[tempSource addObject:dict];
}
[self setSourceArray:tempSource];
[tempSource release];

You can loop thru all of your items in the array and build a side-array only containing names of the day (1), or you can use a predicate and then KVC to extract the days directly (2).
Then join the components of the filtered array into a string.
Solution 1:
NSMutableArray selectedDays = [[NSMutableArray alloc] init];
for(NSDictionary* entry in sourceArray)
{
if (([entry objectForKey:#"isSelected"] boolValue]) {
[selectedDays addObject:[entry objectForKey:#"day"]];
}
}
NSString days = [selectedDays componentsJoinedByString:#", "];
[selectedDays release];
Solution 2:
NSPredicate* filter = [NSPredicate predicateWithFormat:#"SELF.isSelected == 1"]; // not sure about the exact format (no mac here to test right now so you may adapt a bit if it does not work directly)
// get the array of dictionaries but only the ones that have isSelected==1
NSArray selectedEntries = [sourceArray filteredArrayUsingPredicate:filter];
NSArray selectedDays = [selectedEntries valueForKey:#"day"]; // extract the "days" keys of the dictionaries. We have a NSArray of strings then.
NSString days = [selectedDays componentsJoinedByString:#", "];
As a side note, your way of doing this is quite strange. Why having an NSArray of NSDictionaries for this? As this is simple and a static-size array containing only BOOL, you may instead for this particular case simply use C array BOOL selected[7] and nothing more.
Then to have the name of the weekdays you should instead use the methods of NSCalendar/NSDateFormatter/NSDateComponents to get the standard names of the weekdays (automatically in the right language/locale of the user): create an NSDate using a NSDateComponent for which you simply define the weekday component, then use an NSDateFormatter to convert this to a string, choosing a string format that only display the weekday name.
-(void)tableView:(UITableView*)tv didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
// selectedDays is an instance variable in .h declared as
// BOOL selectedDays[7];
selectedDays[indexPath.row] = ! selectedDays[indexPath.row];
[tv reloadData];
}
-(UITableViewCell*)tableView:(UITableView*)tv cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString* kCellIdentifier = #"DayCell";
UITableViewCell* cell = [tv dequeueReusableCellWithIdentifier:kCellIdentifier];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:... identifier:kCellIdentifier]; autorelease];
// configure here every property that is common to all for your cells (text color, etc)
}
// configure here things that will change from cell to cell
cell.accessoryType = selectedDays[indexPath.row] ? UITableViewCellAccessoryTypeCheckmarck : UITableViewCellAccessoryTypeNone;
cell.textLabel.text = weekdayName(indexPath.row);
return cell;
}
// Simple C function to return the name of a given weekday
NSString* weekdayName(int weekday)
{
#if WAY_1
/**** Solution 1 ****/
// Optimization note: You may compute this once for all instead of recomputing it each time
NSDateComponents* comp = [[[NSDateComponents alloc] init] autorelease];
[comp setWeekday:weekday+1]; // weekdays start at 1 for NSDateComponents
NSDate* d = [[NSCalendar currentCalendar] dateFromComponents:comp];
NSDateFormatter* df = [[[NSDateFormatter alloc] init] autorelease];
[df setDateFormat:#"EEEE"]; // format for the weekday
return [[df stringFromDate:d] capitalizedString];
#else
/**** Solution 2 ****/
NSDateFormatter* df = [[[NSDateFormatter alloc] init] autorelease];
NSArray* weekdayNames = [df weekdaySymbols]; // or maybe one of its sibling methods? check what is returned here to be sure
return [weekdayNames objectAtIndex:weekday];
#endif
}

Is there a reason that you avoid NSMutableIndexSet?
It may simplify your code to:
NSArray *daysOfWeek = ...;
NSMutableIndexSet *indexesOfSelectedDays = ...;
NSArray *selectedDays = [daysOfWeek objectsAtIndexes:indexesOfSelectedDays];

Use NSPredicate to create a predicate that selects the items in the array where isSelected is true, and use that to filter the array using NSArray's -filteredArrayUsingPredicate: method. (#Miraaj posted a good example of using a predicate faster than I could type it.) Then take that filtered array and pick out the days, like this:
NSArray *selectedItems = [sourceArray filteredArrayUsingPredicate:somePredicate];
NSArray *days = [selectedItems valueForKey:#"day"];
NSString *daysString = [days componentsJoinedByString#", "];

Related

Date array sorting issue, iPhone sdk

I have an array that contains different date values. And I have used the following code to sort the date array, its done.
combinedArr = [[NSMutableArray alloc]init];
NSInteger counts = [pbTitle count];
for (int i = 0; i < counts; i++) {
CustomObject *customobject2 = [CustomObject customObjectWithName:
[pbTitle objectAtIndex:i] andDate:[pbstartDate objectAtIndex:i]];
[combinedArr addObject:customobject2];
}
[combinedArr sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
return [[(CustomObject*)obj1 date]compare: [(CustomObject*)obj2 date]];
}];
NSLog(#"Results: %#", combinedArr);
Now the result is in the combinedArr, I need to check the each value with current system time and need to load into two different arrays, and load these two arrays into two sections of a tableView. How can I implement that? Please help me to find a solution.
I think that the simplest and the fastest (shorter running time) solution is to create 2 separate arrays from the beginning and sort each one separately.
Like this:
NSDate *currentDate = [NSDate date];
NSMutableArray *pastArray = [[NSMutableArray alloc] init];
NSMutableArray *futureArray = [[NSMutableArray alloc] init];
NSInteger counts = [pbTitle count];
// Fill the arrays
for (int i = 0; i < counts; i++) {
NSDate *customOnjectDate = [pbstartDate objectAtIndex:i];
CustomObject *customobject2 = [CustomObject customObjectWithName:[pbTitle objectAtIndex:i] andDate:customOnjectDate];
NSMutableArray *array = ([customOnjectDate compare:currentDate] == NSOrderedAscending ? pastArray : futureArray);
[array addObject:customobject2];
}
// Sort the arrays
[pastArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [[obj1 date] compare:[obj2 date]];
}];
[futureArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [[obj1 date] compare:[obj2 date]];
}];
// Use the arrays
NSLog(#"pastArray: %#", pastArray);
NSLog(#"futureArray: %#", futureArray);
// Don't forget to release the arrays after you use them
[pastArray release];
[futureArray release];

How to sort NSMutableArray elements?

I have model class which contains NSString's- studentName, studentRank and studentImage. I wanna sort the NSMutableArray according to studentRanks. what I have done is
- (void)uploadFinished:(ASIHTTPRequest *)theRequest
{
NSString *response = nil;
response = [formDataRequest responseString];
NSError *jsonError = nil;
SBJsonParser *json = [[SBJsonParser new] autorelease];
NSArray *arrResponse = (NSArray *)[json objectWithString:response error:&jsonError];
if ([jsonError code]==0) {
// get the array of "results" from the feed and cast to NSArray
NSMutableArray *localObjects = [[[NSMutableArray alloc] init] autorelease];
// loop over all the results objects and print their names
int ndx;
for (ndx = 0; ndx < arrResponse.count; ndx++)
{
[localObjects addObject:(NSDictionary *)[arrResponse objectAtIndex:ndx]];
}
for (int x=0; x<[localObjects count]; x++)
{
TopStudents *object = [[[TopStudents alloc] initWithjsonResultDictionary:[localObjects objectAtIndex:x]] autorelease];
[localObjects replaceObjectAtIndex:x withObject:object];
}
topStudentsArray = [[NSMutableArray alloc] initWithArray:localObjects];
}
}
How can I sort this topStudentsArray according to the ranks scored by the Students and If the two or more student have the same rank, How can I group them.
I did like this
TopStudents *object;
NSSortDescriptor * sortByRank = [[[NSSortDescriptor alloc] initWithKey:#"studentRank" ascending:NO] autorelease];
NSArray * descriptors = [NSArray arrayWithObject:sortByRank];
NSArray * sorted = [topStudentsArray sortedArrayUsingDescriptors:descriptors];
but this is not displaying results properly. please help me to overcome this problem. thanks in advance.
doing something like this might do the trick
Initially sort the arrGroupedStudents in the (ascending/descending) order of studentRank
//Create an array to hold groups
NSMutableArray* arrGroupedStudents = [[NSMutableArray alloc] initWithCapacity:[topStudentsArray count]];
for (int i = 0; i < [topStudentsArray count]; i++)
{
//Grab first student
TopStudents* firstStudent = [topStudentsArray objectAtIndex:i];
//Create an array and add first student in this array
NSMutableArray* currentGroupArray = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
[currentGroupArray addObject:firstStudent];
//create a Flag and set to NO
BOOL flag = NO;
for (int j = i+1; j < [topStudentsArray count]; j++)
{
//Grab next student
TopStudents* nextStudent = [topStudentsArray objectAtIndex:j];
//Compare the ranks
if ([firstStudent.studentRank intValue] == [nextStudent.studentRank intValue])
{
//if they match add this to same group
[currentGroupArray addObject:nextStudent];
}
else {
//we have got our group so stop next iterations
[arrGroupedStudents addObject:currentGroupArray];
// We will assign j-1 to i
i=j-1;
flag = YES;
break;
}
}
//if entire array has students with same rank we need to add it to grouped array in the end
if (!flag) {
[arrGroupedStudents addObject:currentGroupArray];
}
}
Finally your arrGroupedStudents will contain grouped array with equal rank. I have not test run the code so you might need to fix a few things falling out of place. Hope it helps
If you want to display in the order of ranks, you should set the ascending as YES.
NSSortDescriptor * sortByRank = [[NSSortDescriptor alloc] initWithKey:#"studentRank" ascending:YES];
static int mySortFunc(NSDictionary *dico1, NSDictionary *dico2, void *context)
{
NSString *studentName1 = [dico1 objectForKey:#"studentName"];
NSString *studentName2 = [dico2 objectForKey:#"studentName"];
return [studentName1 compare:studentName2];
}
- (IBAction)sortBtnTouched:(id)sender
{
[topStudentsArray sortUsingFunction:mySortFunc context:NULL];
}

SQL select statement equivalent for query on plist

I am trying to figure out the best approach for queries on a plist. Thus, attempting at making an sql equivalent to "SELECT * FROM sometable WHERE somecol = someval AND someothercol = someotherval AND ".... so on and soforth ... using a plist as the sql table equivalent. Coming from Ruby and mysql, this just seems like a lot of code for one simply query. The results come back as expected (at least at first run they did, I have not tested this rigorously) with no errors.
So here is the question: Is there some simple method hiding in the docs somewhere that would make this less clunky?
and if not what is a better approach?
EPFramework.m
// LOAD PLIST AND FILTER MULTIPLE TIMES
-(NSMutableArray *)loadPlistAndFilterMultipleTimes:(NSString *)plist ArrayOfKeys:(NSArray *)arrayOfKeys ArrayOfKeyValues:(NSArray *)arrayOfKeyValues
{
// set the array counts
int arrayOfKeysCount = [arrayOfKeys count];
int arrayOfKeyValuesCount = [arrayOfKeyValues count];
// initialize the array to return
NSMutableArray *arrayFilteredResults = [[[NSMutableArray alloc] init] retain];
// qualify the search
if(arrayOfKeysCount == arrayOfKeyValuesCount && arrayOfKeysCount > 0 && arrayOfKeyValuesCount > 0)
{
// get the plist
NSString *fullFileName = [NSString stringWithFormat:#"%#.plist", plist];
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:fullFileName];
// put the plist records into an array
NSArray *arrayOfDictionaryItemsInPlist = [[NSMutableArray alloc] initWithContentsOfFile:path];
// load our dynamic array for mutability throughout the loops
NSMutableArray *arrayFiltered = [[[NSMutableArray alloc] initWithArray:arrayOfDictionaryItemsInPlist] retain];
// build an array of the final results to return
for(int i=0; i < arrayOfKeysCount; i ++)
{
// initialize this loops search criteria
NSString *key = [arrayOfKeys objectAtIndex:i];
id value = [arrayOfKeyValues objectAtIndex:i];
// set the filter
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", key, value];
// filter the result
arrayFilteredResults = [[[NSArray alloc] initWithArray:[arrayFiltered filteredArrayUsingPredicate:predicate]] retain];
}
} else {
NSLog(#"arrOfKeys count does not match arrayOfKeyValues count"); // the search did not qualify
}
// return the results
return arrayFilteredResults;
// release the allocated memory
[arrayFilteredResults release];
}
IndexController.m
NSArray *arrayOfKeys = [NSArray arrayWithObjects:
[NSString stringWithString:#"recordset"],
[NSString stringWithString:#"ep_object_attribute_id"],
nil];
NSArray *arrayOfKeyValues = [NSArray arrayWithObjects:
[NSString stringWithString:#"1778587279"],
[NSNumber numberWithInt:133],
nil];
NSMutableArray *arrayOfResult = [epFrameWork loadPlistAndFilterMultipleTimes:#"FormEntries" ArrayOfKeys:arrayOfKeys ArrayOfKeyValues:arrayOfKeyValues];
NSLog(#"arrayOfResult: %#", arrayOfResult);
Here's the link for fmdb. Should be quick to pick it up, be faster and you get a real database instead of simulating one with plists :)
https://github.com/ccgus/fmdb
Hope that helps

Objective-C iPhone - Ordering data within multiple sections of a UITableView dataSource

For the purpose of asking this question about ordering. The following MyObject class returns an instance with random generated category names.
I use the following dataSource methods:
numberOfSections accessed with [dataSource count].
titleForSection accessed with [[dataSource objectAtIndex:indexPath.section] valueForKey:#"categoryName"].
numberOfRowsInSection accessed with [[[dataSource objectAtIndex:indexPath.section] valueForKey:#"myObjects"] count].
And finally, the MyObject for each row is accessed with [[[dataSource objectAtIndex:indexPath.section] valueForKey:#"myObjects"] objectAtIndex:indexPath.row] on the cellForRowAtIndexPath method.
I use the following code to create a dataSource that displays 9 section categories, however I'm a little stuck on the ordering of these categories and the data within. Assume there's an NSDate property as part of the MyObject class.
Question: How would I go about using this to display the records in descending order?
- (void)createDatasource
{
NSInteger numberOfObjects = 10;
NSMutableArray *objects = [NSMutableArray arrayWithCapacity:numberOfObjects];
NSMutableArray *categories = [NSMutableArray arrayWithCapacity:numberOfObjects];
for (int i = 0; i < numberOfObjects; i++)
{
MyObject *obj = [[MyObject alloc] init];
[objects addObject:obj];
[categories addObject:obj.category];
[obj release];
}
NSSet *set = [NSSet setWithArray:categories];
NSMutableArray *dataSource = [[NSMutableArray alloc] initWithCapacity:[set count]];
for (NSString *categoryString in set)
{
NSMutableDictionary *mainItem = [[NSMutableDictionary alloc] initWithObjectsAndKeys:nil, #"categoryName", nil, #"myObjects", nil];
NSMutableArray *mainItemMyObjects = [NSMutableArray array];
[mainItem setValue:categoryString forKey:#"categoryName"];
for (MyObject *obj in objects)
{
if ([obj.category isEqualToString:categoryString])
{
[mainItemMyObjects addObject:obj];
}
}
[mainItem setValue:mainItemMyObjects forKey:#"myObjects"];
[dataSource addObject:mainItem];
[mainItem release];
}
NSLog (#"objects = %#\ncategories = %#\nset = %#\ndatasource = %#", objects, categories, set, dataSource);
}
Easiest would be to sort your arrays, using NSMutableArray's sorting mutators or NSArray's sorting methods. Otherwise you'd have to construct some sort of mapping from input indices to dataSource indices for use by the various data source methods.
Edit Requested sample code for sorting, something like this should work. I assume you are wanting to sort everything by a property named date on the MyObject.
// First, sort the myObject mutable array in each category
for (NSDictionary *d in dataSource) {
[[d valueForKey:#"myObjects"] sortUsingComparator:^NSComparisonResult(id o1, id o2){
// Compare dates. NSDate's 'compare:' would do ascending order, so if we just
// reverse the order of comparison they'll come out descending.
return [[o2 date] compare:[o1 date]];
}];
}
// Second, sort the categories by the earliest dated object they contain
[dataSource sortUsingComparator:^NSComparisonResult(id o1, id o2){
// Extract the first object from each category's array, which must be the
// earliest it contains due to the previous sort.
MyObject *myObject1 = [[o1 valueForKey:#"myObjects"] objectAtIndex:0];
MyObject *myObject2 = [[o2 valueForKey:#"myObjects"] objectAtIndex:0];
// Compare dates, as above.
return [[myObject2 date] compare:[myObject1 date]];
}];

Converting table view to have sections

I have a table view, which has its data source from an array that contains names of people.
Now to make it easy to find people, I want to section the table view so that it has the letter A-Z on the right hand side, just like the Address Book app.
But my current array just contains a collection of NSStrings. How do I split them so that they are grouped by the first letter of the names? Is there any convenient way to do it?
EDIT: If anyone's interested in my final code:
NSMutableArray *arrayChars = [[NSMutableArray alloc] init];
for (char i = 'A'; i <= 'Z' ; i++) {
NSMutableDictionary *characterDict = [[NSMutableDictionary alloc]init];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for (int k = 0; k < [myList count]; k++) {
NSString *currentName = [[friends objectAtIndex:k] objectForKey:#"name"];
char heading = [currentName characterAtIndex:0];
heading = toupper(heading);
if (heading == i) {
[tempArray addObject:[friends objectAtIndex:k]];
}
}
[characterDict setObject:tempArray forKey:#"rowValues"];
[characterDict setObject:[NSString stringWithFormat:#"%c",i] forKey:#"headerTitle"];
[arrayChars addObject:characterDict];
[characterDict release];
[tempArray release];
}
At the end of the function I'll have:
arrayChars [0] = dictionary(headerTitle = 'A', rowValues = {"adam", "alice", etc})
arrayChars[1] = dictionary(headerTitle = 'B', rowValues = {"Bob", etc})
Thank you everyone for your help!
You can use a dictionary to sort them, so create an array with all the letters you want to sort and a array with nil objects to initialize the dictionary
NSArray *names = #[#"javier",#"juan", #"pedro", #"juan", #"diego"];
NSArray *letters = #[#"j", #"p", #"d"];
NSMutableArray *objects = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < [letters count]; ++i)
{
[objects addObject:[[NSMutableArray alloc] init]];
}
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:letters];
Then you must find the first letter if the word and put that word into the corresponding key in the dictionary
for (NSString *name in names) {
NSString *firstLetter = [name substringToIndex:1];
for (NSString *letter in letters) {
if ([firstLetter isEqualToString:letter]) {
NSMutableArray *currentObjects = [dictionary objectForKey:letter];
[currentObjects addObject:name];
}
}
}
To check you can print directly the dictionary
NSLog(#"%#", dictionary);
Then is your work to fill your sections in the tableview using the dictionary