Use existing NSArray object properties to create a new NSArray for sectioned tableView - iphone

So I have the kind of classic situation where I want to group my tableView by Month/Year. I have a member of my conference object called beginDateSearchString that I use to put different conference into buckets; my problem is in the next part where I try and fail to use a NSSortDescriptor to sort each bucket by beginDate (which is a date).
I am getting an error related to unsorted not being able to receive sort descriptor type selectors.
Here is the disgusting code:
- (NSArray *)arrayOfDateSortedEvents {
NSMutableArray *sortedArray = [[NSMutableArray alloc] init];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
//place into buckets
for (WSConference *conference in self.arrayOfEvents) {
if (![dictionary objectForKey:[conference beginDateSearchString]]) {
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:conference,nil];
[dictionary setObject:array forKey:[conference beginDateSearchString]];
}
else {
[[dictionary objectForKey:[conference beginDateSearchString]] addObject:conference];
}
}
//sort each bucket by descriptor beginDate
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"beginDate" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:descriptor];
for (NSMutableArray *unsorted in dictionary) {
[unsorted sortUsingDescriptors:sortDescriptors];
}
// now, unkey and add dictionary in order
while ([dictionary count] > 0) {
NSString *lowest = nil;
for (NSMutableArray *array in dictionary) {
if (!lowest)
lowest = [[dictionary allKeysForObject:array] objectAtIndex:0];
else {
if ([(WSConference *)[array objectAtIndex:0] beginDate] < [[dictionary objectForKey:lowest] beginDate])
lowest = [[dictionary allKeysForObject:array] objectAtIndex:0];
}
}
[sortedArray addObject:[dictionary objectForKey:lowest]];
[dictionary removeObjectForKey:lowest];
}
return sortedArray;
}

You want to probably filter the array in addition to sorting. See NSPredicate and the NSArray method -filteredArrayUsingPredicate: Then create an eventsByDateArray of the eventArrays created by the filter. Then in your table view delegate for creating the cells, if everything is ordered properly, the first section would represent the date of the events in the eventArray that is the first object of the eventsByDateArray and the table rows would consist of the events in the eventArray. And so on for each date.
Added
Your fast enumeration is incorrect. You enumerate through the keys of the dictionary. So in your code unsorted equals each of the keys as it enumerates. This is a GREAT lesson to everyone. It does not matter how you 'type' a variable. When Objective-C compiles it turns them all into id. So NSMutableArray *unsorted is not an NSMutableArray unless it is assigned to an NSMutableArray. If you assign unsorted to an NSString it will be an NSString. The fast enumerator for a dictionary works using the keys. So, in this case, unsorted becomes an NSString.
Instead of:
for (NSMutableArray *unsorted in dictionary) {
[unsorted sortUsingDescriptors:sortDescriptors];
}
you should have this:
for (id key in dictionary) {
NSMutableArray *unsorted = [dictionary objectForKey:key];
[unsorted sortUsingDescriptors:sortDescriptors];
}

Related

Delete only one item from array having same multiple values

There is an array in my app having multiple same values in it. I need to delete only one value at a time from array whether it has same more values in it.
Level1 Business,
Level2 Economy,
Level2 Economy,
Level1 Business
How this can be achieved, and main thing is that these values are dynamic these can be more or less also. Please guide for above.
Below is what i tried.
if([arr containsObject:[NSString stringWithFormat:#"%d",ind]]){
[arr removeObject:[NSString stringWithFormat:#"%d",ind]];
}
This thing removes all similar entries, not required. Thanks in advance.
try like this,
NSArray *array = [NSArray arrayWithObjects:#"Level1 Business", #"Level2 Economy", #"Level2 Economy", #"Level1 Business", nil];
NSMutableArray *mainarray=[[NSMutableArray alloc]initWithArray:array];
int n=[mainarray indexOfObject:#"Level2 Economy"];//it gives first occurence of the object in that array
if(n<[mainarray count]) // if the object not exist then it gives garbage value that's why here we have to take some condition
[mainarray removeObjectAtIndex:n];
NSLog(#"%#",mainarray);
O/P:-
(
"Level1 Business",
"Level2 Economy",
"Level1 Business"
)
As you say,
[array removeObject:#"SomeObject"];
removes all instances of where isEqual: returns YES. To remove only the first instance, you can use something like
NSUInteger index = [array indexOfObject:#"SomeObject"];
if(index != NSNotFound) {
[array removeObjectAtIndex:index];
}
Use [arr removeObjectAtIndex:yourIndex ] to remove your object at perticular postion at dynamic
Sample Code :
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
NSUInteger obj = [arr indexOfObject:#"hi"]; //Returns the lowest integer of the specified object
[arr removeObjectAtIndex:obj]; //removes the object from the array
NSLog(#"%#",arr);
In your Case :
if([arr containsObject:[NSString stringWithFormat:#"%d",ind]])
{
NSUInteger obj = [arr indexOfObject:[NSString stringWithFormat:#"%d",ind]]; //Returns the lowest integer of the specified object
[arr removeObjectAtIndex:obj];
}
Here your requirement is like definition of NSSet, which contains unique objects only.
But this will implies only if both the same value objects, are really in referring to same memory location as well.
If this is the case then and then, you can try code mentioned below:
// create set from an array
NSSet *telephoneSet = [NSSet setWithArray: myArray];
// create array from a set
NSMutableArray *array = [NSMutableArray arrayWithArray:[set allObjects]];
I don't know whether it will work for your requirement or not. But for that, it would be required to check the object equality level.
Still it might help you as an less line of code.
NSMutableArray *uniques= [[NSMutableArray alloc] init];
for (NSString *word in duplicateWordsArray){
if (!uniques.contains(word)){
[ uniques addObject:word];
}
}
I wrote this from my phone so it isn't formatted for code, but this will do it for you quickly and you'll have an array (uniquearray) that has unique words. Then you can use that one or set your original array = to unique array
NSArray *input = [NSArray arrayWithObjects:#"Level1 Business", #"Level2 Economy", #"Level2 Economy", #"Level1 Business", nil];
NSMutableArray *output = [[NSMutableArray alloc] init];
[output addObject:[input objectAtIndex:0]];
for(NSString *value in input) {
if(![output containsObject:value])
[output addObject:value];
}

Sorting NSMutableDictionary data

In my application I am having a dictionary which contains Keys A-Z ie 26 characters which are not in sorted ie for eg A,B,C,......
I want to Sort first The dictionary keys alphabetically and also the sort the data related to each key and then again store that in same dictionary.
NSArray *myKeys = [mGlossaryDict allKeys];
NSArray *op = [myKeys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
until now here I have an mGlossaryDict which is an Mutabledictionary which is sorted but i need to sort the data from each of the key.
Please help me out.
NSArray *myKeys = [mGlossaryDict allKeys];
NSArray *sortedKeys = [myKeys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableArray *sortedValues = [[[NSMutableArray alloc] init] autorelease];
for(id key in sortedKeys) {
id object = [myGlossaryDict objectForKey:key];
[sortedValues addObject:object];
}

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]];
}];

Table crashes when sorting the data multiple times

I have a tableview with a navigationBar with a segmentedControl on the top of the view. I have set up the segmentedControl with buttons that sort the table by either "FirstName" or "LastName". It works perfectly the first 2-4 of times you press the sorting buttons, but then the app crashes.
The debugger and console seem to be of no help finding the source of the bug. Does anyone see any glaring mistakes in my code?
Here is my code below, let me know if you have any questions. Thanks!
- (IBAction)sortingSegmentAction:(id)sender{
NSString *keyToSortBy = [NSString alloc];
if([sender selectedSegmentIndex] == 0)
{
self.sortingSegmentActionPressed = 0;
keyToSortBy = #"FirstName";
}
else if([sender selectedSegmentIndex] == 1)
{
self.sortingSegmentActionPressed = 1;
keyToSortBy = #"LastName";
}
//Create the sort descriptors
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:keyToSortBy ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
//Sort allSubItams by the values set in the sort descriptors
NSArray *sortedArray;
self.sortedArray = [allSubItems sortedArrayUsingDescriptors:sortDescriptors];
//Recreate the data structure by putting the newly sorted items into a dictionary sorted by inital letters.
NSDictionary *eachItemList; //A DICTIONARY FOR PUTTING ALL THE DATA FOR EACH ITEM IN IT'S OWN SECTION
NSMutableDictionary *tempSectionedDictionaryByFirstLetter = [[NSMutableDictionary alloc] init];
for (eachItemList in sortedArray) //eachElementList is a dictionary with a section for each item
{
NSDictionary *aDictionary = [[NSDictionary alloc] initWithDictionary:eachItemList];
NSString *firstLetterString;
firstLetterString = [[aDictionary valueForKey:keyToSortBy]substringToIndex:1];
NSMutableArray *existingArray;
if (existingArray = [tempSectionedDictionaryByFirstLetter valueForKey:firstLetterString])
{
[existingArray addObject:eachItemList];
} else {
NSMutableArray *tempArray = [NSMutableArray array];
[tempSectionedDictionaryByFirstLetter setObject:tempArray forKey:firstLetterString];
[tempArray addObject:eachItemList];
}
[aDictionary release];
[eachItemList release];
}
//Set the data source for the table (sectionedDictionaryByFirstLetter) to tempSectionedDictionaryByFirstLetter.
self.sectionedDictionaryByFirstLetter = tempSectionedDictionaryByFirstLetter;
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.sectionedDictionaryByFirstLetter allKeys] sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;
[self.tableView reloadData];
[keyArray release];
[tempSectionedDictionaryByFirstLetter release];
}
Don't release eachItemList at the end of your loop. You did not explicitly allocate it in this context so you shouldn't release it.
The for (object in array) loop gives you a reference to the object in the array, not a copy. By sending a release message to this reference, you are decrementing the retain count of this object while it is still in the array. After a few times (depending on how many times the object has been retained, NSArray for example retains objects when they are added to the array) it's retain count will reach 0, and it will then become deallocated and you'll get crashes regarding unrecognised selectors or EXC_BAD_ACCESS and possibly other kinds of errors.

Sort Array by Key

I have two arrays used in a small game.
If the player gets a score above a certain value their name & score gets output via
an UILabel.
NSArray *namesArray = [mainArray objectForKey:#"names"];
NSArray *highScoresArray = [mainArray objectForKey:#"scores"];
I need the UILabels to display with the highest score in descending order, with the corresponding name. I've used an NSSortDescriptor to sort the score values numerically.
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"self"
ascending:NO] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedScore = [[NSArray alloc]init];
sortedScore = [scoresArray sortedArrayUsingDescriptors:sortDescriptors];
NSMutableArray *scoreLabels = [NSMutableArray arrayWithCapacity:10];
[scoreLabels addObject:scoreLabel1];
......
NSUInteger _index = 0;
for (NSNumber *_number in sortedScore) {
UILabel *_label = [scoreLabels objectAtIndex:_index];
_label.text = [NSString stringWithFormat:#"%d", [_number intValue]];
_index++;
}
This works well enough as the scores now display in descending order.
The problem is that I need the corresponding name to also display according in the new sorted order.
I cant use the same sort selector and I don't wont to sort them alphabetically, they need
to correspond to the name/score values that were first input.
Thanks in advance
You need to put the name and the score together into a single instance of NSDictionary, and then have an NSArray of those NSDictionary instances. Then when you sort by score, you can pull up the corresponding name.