How to sort an array of NSDate(in NSMutableArray)? - sorting

Can anyone show me how to sort an array(NSMutableArray) of NSDate in order for the actual dates?
Thanks!

I believe you want to use something like -NSMutableArray sortUsingSelector: and pass in #selector(compare:). Ie, assuming you had an NSMutableArray of NSDates named *dateArray*:
[dateArray sortUsingSelector:#selector(compare:)];

Here you go just modify some part of code for your requirement
- (NSArray *)sortedWeightEntriesByWeightDate:(NSArray *)unsortedArray {
NSMutableArray *tempArray = [NSMutableArray array];
NSMutableArray *sortedArray = [NSMutableArray arrayWithCapacity:0];
#try {
for(int i = 0; i < [unsortedArray count];i++) {
NSDateFormatter *df = [[NSDateFormatter alloc]init];
MyDataModal *entry = [unsortedArray objectAtIndex:i];
[df setDateFormat:#"yyyy-MM-dd"];
NSDate *date = [df dateFromString:entry.weightDate];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
if(date) {
[dict setObject:entry forKey:#"entity"];
[dict setObject:date forKey:#"date"];
[tempArray addObject:dict];
}
[df release];
}
NSInteger counter = [tempArray count];
NSDate *compareDate;
NSInteger index;
for(int i = 0 ; i < counter; i++) {
index = i;
compareDate = [[tempArray objectAtIndex:i] valueForKey:#"date"];
NSDate *compareDateSecond;
for(int j = i+1 ; j < counter; j++) {
compareDateSecond=[[tempArray objectAtIndex:j] valueForKey:#"date"];
NSComparisonResult result = [compareDate compare:compareDateSecond];
if(result == NSOrderedDescending) {
compareDate = compareDateSecond;
index=j;
}
}
if(i!=index)
[tempArray exchangeObjectAtIndex:i withObjectAtIndex:index];
}
NSInteger counterIndex = [tempArray count];
for(int i = 0; i < counterIndex ; i++) {
[sortedArray addObject:[[tempArray objectAtIndex:i] valueForKey:#"entity"]];
}
}
#catch (NSException * e) {
NSLog(#"An exception occured while sorting weight entries by date");
}
#finally {
return [NSArray arrayWithArray:sortedArray];
}
}

Related

UIButton Randomize Title With Single Characters

The code below takes a string, adds each letter to an array and shuffles that array and shows the end result in a label. That works well. But I'd like for each character to contain a single character of the shuffled string. Right now it almost works, but it always repeats the characters. Like instead of having a series of 6 buttons with their titles: L e a g u e, the code generates repeated characters like: Leaauu.
My code is this:
- (IBAction)shuffleButttonTitles:(id)sender {
// The mutable array must be created here to create a new instance each time the button is tapped
letters = [[NSMutableArray alloc] init];
str = #"League";
length = str.length;
NSString *letter;
UIButton *button;
// First loop through the string and add each letter to an array
for (int i = 0; i < length; i++) {
letter = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[letters addObject:letter];
}
// Shuffle the string for the label/buttons
for (int i = 0; i < length; i++) {
int value = arc4random() % (length - 1);
[letters exchangeObjectAtIndex:i withObjectAtIndex:value];
//Create the button and shuffle the letters for their titles
button = [[UIButton alloc] initWithFrame:CGRectMake(50 * i, 350, 44, 44)];
// HERE THE CODE REPEATS THE CHARACTERS
[button setTitle:[letters objectAtIndex:i] forState:UIControlStateNormal];
//Store the button in our array
[myButtons addObject:button];
NSLog(#"Letters in Array: %lu", letters.count);
}
for (UIButton *button in myButtons){
[button setBackgroundColor:[UIColor redColor]];
[self.view addSubview:button];
}
// Now we set the randomized title to the label
NSString *results = [letters componentsJoinedByString:#""];
string.text = results;
}
After some searching on the web I've figured it out. I post the complete code for others. This code takes a random string from the Characters.txt file and shuffles that string. Then it rotates the tiles slighty. You can enter the correct word when you have figured out the anagram, which then shows an alert view if you got it or didn't get it.
#define kTileSpacing 20
#define randomf(minX,maxX) ((float)(arc4random() % (maxX - minX + 1)) + (float)minX)
#interface ViewController ()
#end
#implementation ViewController
{
}
#synthesize progressView;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Anagrams" ofType:#"plist"];
dictionary = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSString *quotesFile = [[NSBundle mainBundle] pathForResource:#"Characters" ofType:#"txt"];
fileContents = [NSString stringWithContentsOfFile:quotesFile encoding:NSUTF8StringEncoding error:NULL];
// [txtField becomeFirstResponder];
//[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkWord:) name:nil object:nil];
}
- (IBAction)clear:(id)sender {
quoteArray = [fileContents componentsSeparatedByString:#"\n"];
NSString *quoteToDisplay;
currentQuestion = arc4random() % quoteArray.count;
quoteToDisplay = [quoteArray objectAtIndex: currentQuestion];
welldone.text = quoteToDisplay;
txtField.text = nil;
[txtField becomeFirstResponder];
for (UILabel *lbl in myButtons) {
[lbl removeFromSuperview];
}
}
- (IBAction)ShuffleString:(id)sender {
[self clear:nil];
// The mutable array must be created here to create a new instance each time the button is tapped
charactersArray = [[NSMutableArray alloc] init];
indexArray = [[NSMutableArray alloc] init];
myButtons = [[NSMutableArray alloc] init];
// 1. Shuffle the plist with the words to form anagrams from
currentQuestion = arc4random() % quoteArray.count;
str = [quoteArray objectAtIndex: currentQuestion]; //[[dictionary objectAtIndex:currentQuestion] objectForKey:#"Anagram"];
length = str.length;
NSString *letter;
// 2. Loop throught the chosen word and break it down into its letters and add them to an array
for (int i = 0; i < str.length; i++) {
// [charactersArray removeObjectAtIndex:i];
letter = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[charactersArray addObject:letter];
// NSLog(#"Number of letters: %#", charactersArray);
}
while ([charactersArray count]) {
int randomizing = arc4random() % [charactersArray count];
[indexArray addObject:[charactersArray objectAtIndex:randomizing]];
[charactersArray removeObjectAtIndex:randomizing];
// NSLog(#"NO REPEAT SHUFFLE: %lu", (unsigned long)indexArray.count);
}
/***************/
CGFloat staticY = self.view.bounds.size.height / 9 * 1; // Static X for all buttons.
CGFloat staticWidth = 46; // Static Width for all Buttons.
CGFloat staticHeight = 46; // Static Height for all buttons.
CGFloat staticPadding = 10; // Padding to add between each button.
float tileSize = ceilf( self.view.bounds.size.width / str.length );
NSLog(#"size %f", tileSize);
CGFloat xOffset = (self.view.bounds.size.width - str.length * (44+staticPadding));
NSLog(#"xOffset %f", tileSize);
xOffset = tileSize/ 2;
for (int i = 0; i < str.length; i++) {
singleCharacterLabel = [[UILabel alloc] init];
singleCharacterLabel.textAlignment = NSTextAlignmentCenter;
singleCharacterLabel.font = [UIFont fontWithName:#"Verdana-Bold" size:21];
singleCharacterLabel.frame = CGRectMake((staticPadding + (i * (staticHeight + staticPadding))), staticY, staticWidth, staticHeight);
// NSLog(#"X: %f", (staticPadding + (i * (staticHeight + staticPadding))));
//singleCharacterLabel.center = CGPointMake(i * 50 + self.view.bounds.origin.x + self.view.bounds.size.width /3, 80); // i * int +self... int = space between labels. Here it is '50'
// singleCharacterLabel.center = CGPointMake(self.view.bounds.size.width * i, self.view.bounds.size.height / 5 * 1); // 1/4th down from the top
singleCharacterLabel.layer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"Tile.png"]].CGColor;
NSString *anagramString = [indexArray objectAtIndex:i];
singleCharacterLabel.text = anagramString;
[myButtons addObject:singleCharacterLabel];
//1
//set random rotation of the tile
//anywhere between -0.2 and 0.3 radians
float rotation = randomf(0,50) / (float)100 - 0.2;
singleCharacterLabel.transform = CGAffineTransformMakeRotation( rotation );
//2
//move randomly upwards
int yOffset = (arc4random() % 10) - 10;
singleCharacterLabel.center = CGPointMake(singleCharacterLabel.center.x, singleCharacterLabel.center.y + yOffset);
[self.view addSubview:singleCharacterLabel];
//NSLog(#"LOOP: %#", anagramString);
}
}
- (IBAction)checkWord:(id)sender {
if (([txtField.text isEqual:str])) {
alertCorrect = [[UIAlertView alloc] initWithTitle:#"" message:#"Well done!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Next", nil];
[alertCorrect show];
} else {
alertWrong = [[UIAlertView alloc] initWithTitle:#"" message:#"Sorry, try again." delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alertWrong show];
}
// NSLog(#"String is: %lu", str.length);
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView == alertCorrect) {
if (buttonIndex == 0) {
[self ShuffleString:nil];
NSLog(#"next");
}
}
if (alertView == alertWrong) {
if (buttonIndex == 1) {
// Wrong answer. Close view and let user try again
}
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (txtField.text.length == length) {
[self checkWord:nil];
NSLog(#"You entered %lu characters", length);
}
return YES;
}

Block variable gets null outside of block - xcode

Im trying to carry over a block variable and put it in my convenience initializer but the variable returns null, can someone show me the right way of doing it, Thanks, also please check if I have done my NSComparison Result correctly, here's the code,
- (void) retrieveData
{
NSURL *url = [NSURL URLWithString:jsonFile];
NSData *data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_salesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _jsonArray.count; i++) {
NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:#"name"];
NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:#"address"];
NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:#"postcode"];
__block NSString *distance;
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocation *myLocation = self.manager.location;
CLLocationDistance miles = [location distanceFromLocation:myLocation];
//this is the variable i want in my convenience init.
distance = [NSString stringWithFormat:#"%.1f m", (miles/1609.344)];
}
}];
[_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]];
}
[_salesArray sortUsingComparator:
^NSComparisonResult(id obj1, id obj2){
sales *p1 = (sales *)obj1;
sales *p2 = (sales *)obj2;
if (p1.postcode > p2.postcode) {
return (NSComparisonResult)NSOrderedDescending;
}
if (p1.postcode < p2.postcode) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}
];
[self.tableView reloadData];
}
Thanks in advance to anyone that can help, this happens to be the last part of my app until completion :)
-[CLGeocoder geocodeAddressString:completionHandler:] runs the completion handler asynchronously and it won't run the block before the rest of that function finishes. distance doesn't "get null"; rather, it hasn't been set yet.
your block is run AFTER the function completes ... doesn't matter if the 'line of code' is after or before the block.
the solution is to have to block call a 'continuation' function:
e.g. instead of - (void) retrieveData
have - (void) beginRetrieveData, which calls the block and then have a - (void) finishRetrieveData, which is called by the block and doesn't run before the block ;)
here is how your code could look:
- (void) beginRetrieveData
{
NSURL *url = [NSURL URLWithString:jsonFile];
NSData *data = [NSData dataWithContentsOfURL:url];
_jsonArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_salesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _jsonArray.count; i++) {
NSString *sID = [[_jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString *sName = [[_jsonArray objectAtIndex:i] objectForKey:#"name"];
NSString *sAddress = [[_jsonArray objectAtIndex:i] objectForKey:#"address"];
NSString *sPostcode = [[_jsonArray objectAtIndex:i] objectForKey:#"postcode"];
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:sPostcode completionHandler:^(NSArray *placemarks, NSError *error) {
if (error == nil && placemarks.count > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocation *myLocation = self.manager.location;
CLLocationDistance miles = [location distanceFromLocation:myLocation];
//this is the variable i want in my convenience init.
NSString *distance = [NSString stringWithFormat:#"%.1f m", (miles/1609.344)];
[self finishRetrieveData:distance]; //continue
}
}];
}
}
- (void) finishRetrieveData:(NSString*)distance
{
[_salesArray addObject:[[sales alloc] initWithSales:sID andName:sName andAddress:sAddress andPostcode:distance]];
}

MapKit and adding new pins after a MKLocalSearch

I'm running a MKLocalSearch request and when I find POIs I want to drop a pin based on the coordinates of the POIs I find. NSLog(#"annotations %d",[mapView.annotations count]); returns 0 even with valid coordinates for the POIs
- (void)resultsForButtonTapped:(NSString *)search
{
arrayOfPOIs = [[NSMutableArray alloc] init];
arrayOfAnnotaions = [[NSMutableArray alloc] init];
NSLog(#"%#",search);
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
MKCoordinateRegion region;
request.naturalLanguageQuery = search;
request.region = region;
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
localsearch = [[MKLocalSearch alloc] initWithRequest:request];
[localsearch startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (error != nil) {
return;
}else{
if ([response.mapItems count] == 0) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"No Results",nil)
message:nil
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK",nil) otherButtonTitles:nil] show];
return;
}
for (MKMapItem *item in response.mapItems)
{
NSString *name = item.name;
NSLog(#"%#",name);
[arrayOfPOIs addObject:name];
}
results = response;
NSLog(#"%d",[arrayOfPOIs count]);
for (int i=0; i<[results.mapItems count]; i++) {
MKMapItem* itemPOI = results.mapItems[i];
NSLog(#"Result: %#",itemPOI.placemark.name);
NSLog(#"Result: %#",itemPOI.placemark.name);
MKPlacemark* annotation= [[MKPlacemark alloc] initWithPlacemark:itemPOI.placemark];
MKPointAnnotation *marker = [MKPointAnnotation new];
marker.coordinate = annotation.coordinate;
[mapView addAnnotation:marker];
NSLog(#"annotations %d",[mapView.annotations count]);
}
[self loadTheDataAgain];
}
}];
// NSLog(#"outside the block, the count is %lu",(unsigned long)[arrayOfPOIs count]);
NSLog(#"stuff %#",arrayOfAnnotaions);
}

Xcode: How to find out what NSLog automatically casts into?

Hello I am trying to save and load my game data. It cannot read an integer properly from a file, however it can read a string properly though...
Strangely enough when I print the number with NSLog it works!. However if I try to use the value or pass it into another value it doesn't.
Now I could 'fix' this by converting all the numbers into strings and then saving it, then loading the strings and converting it back to numbers. However I would like to know the proper way of doing it. Also there are 1600 tiles not including other data so that would be a noticeable overhead.
Can someone tell me what I am doing wrong or what NSLog is doing automatically?
Below is the code section relevant to the question. mapData[i][j] is an array of integers representing whats on a tile.
-(void)saveGame
{
NSLog(#"%s", "Saved");
NSString *temp = [self saveFilePath];
NSLog(#"%s %#", "FilePath", temp);
NSMutableArray *values = [NSMutableArray arrayWithCapacity:1602];
int i = 0;
int j = 0;
int a = 0;
for(i = 0; i < 40; i++)
{
for(j = 0; j < 40; j++)
{
//values[i] = mapData[j][k];
[values addObject:[NSNumber numberWithInteger:mapData[i][j]]];
NSLog(#"%d, %d", i, j);
NSLog(#"%s, %d", "Map Data: ", mapData[i][j]);
NSLog(#"%#", [values objectAtIndex:a]);
a++;
}
}
// test letter OK
NSString * letterA = #"a";
[values addObject:[NSString stringWithString:letterA]];
// test independant number
NSInteger aNum = 888;
[values addObject:[NSNumber numberWithInteger:aNum]];
NSArray *value = [NSArray arrayWithArray:values];
// test conversion OK
a = 0;
for(i = 0; i < 40; i++)
{
for(j = 0; j < 40; j++)
{
NSLog(#"%#", [value objectAtIndex:a]);
a++;
}
}
NSLog(#"%#", [value objectAtIndex:a]); // letter OK
a++;
NSLog(#"%#", [value objectAtIndex:a]); // number OK
[value writeToFile:[self saveFilePath] atomically:YES]; // atomically means all or nothing. i.e power fail will not save half
}
-(NSString *)saveFilePath
{
NSArray * path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[path objectAtIndex:0] stringByAppendingPathComponent:#"savefile.plist"];
}
-(void)loadGame
{
NSLog(#"%s", "Loaded");
NSString *loadPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:loadPath];
if(fileExists)
{
NSArray *values = [[NSArray alloc] initWithContentsOfFile:loadPath];
int a = 0;
for(int i = 0; i < 40; i++)
{
for(int j = 0; j < 40; j++)
{
//mapData[i][j] = [values objectAtIndex:a];
//mapData[i][j] = (NSInteger)[values objectAtIndex:a];
NSInteger * tempNumber = [values objectAtIndex:a];
NSLog(#"%#", tempNumber); // YES!
int tempInt = (int)tempNumber;
NSLog(#"%d", tempInt); // no
mapData[i][j] = tempNumber;
NSLog(#"%d, %d, %d", a , i, j);
NSLog(#"%s, %d", "Loaded Map Data: ", mapData[i][j]); // no
a++;
}
}
// string works but integer doesnt...
NSString * letter;
letter = [values objectAtIndex:a];
NSLog(#"%#", letter); // YES
// this number OK - needs pointer AND NSLog...
a++;
NSInteger * number;
number = [values objectAtIndex:a];
NSLog(#"%#", number); // YES
}
}
Nvm I figured it out. NSLog was automatically unwrapping the numbers for me. Manually unwrapping will display the correct numbers instead of memory addresses.

Iterate through array comparing dictionary values

I have a NSMutableArray *dataArray containing NSDictionaries. I want to iterate through the array in order to create a new NSMutableArray *tempDataArray with objects from the first array if the searchString matches one of the dictionaries values.
Here is my code:
self.tempDataArray = [[NSMutableArray alloc] init];
for (int i = 0; i < kDelegate.dataArray.count; i++) {
NSDictionary *dict = [kDelegate.dataArray objectAtIndex:i];
for (int j = 0; j < [dict allValues].count ; j++) {
if ([[[[dict allValues] objectAtIndex:j]stringValue] isEqualToString:searchString]) {
[self.tempDataArray addObject:dict];
}
}
}
Now this doesn't work because the dictionary also contains a NSDate. At least that is what I am reading from the error message:
-[__NSTaggedDate stringValue]: unrecognized selector sent to instance 0x41b7809e2600000d
Which I dont understand, because I do declare all the values as stringValue.
That was the first part of my problem. The second half is that this only searches for an exact match but I would rather have a search determining if the dictionaries values contain a substring.
Anyway, thanks for all your help!
(untested):
self.tempDataArray = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < kDelegate.dataArray.count; i++) {
NSDictionary *dict = [kDelegate.dataArray objectAtIndex:i];
NSArray *allValues = [dict allValues];
for (NSUInteger j = 0; j < allValues.count; j++) {
NSObject *obj = [allValues objectAtIndex:j];
if ([obj isKindOfClass:[NSString class]]) {
NSRange range = [(NSString *)obj rangeOfString:searchString];
if (range.location != NSNotFound) {
[self.tempDataArray addObject:dict];
}
}
}
}

Resources