Currently working on finding the best solution to find the smallest time interval between the current time and a date inside of my array.
I have a method that takes a NSArray and returns an NSArray. The method does the following:
Loops through array sorted by time (0 index is closest to current time)
Find smallest interval by finding the delta and comparing
Grab that index, fetch it, return a dictionary
I started to look in to using timeIntervalSinceDate and so on instead of the manual work I am using below.
The array looks something like this:
tideSummary: [{
'type' : 'High Tide',
'pretty' : 'January 16 at 5:13PM EST',
'epoch' : '325267782',
...
}]
Does this code seem to be bloated for what its trying to do? I feel there is a good chunk of duplication to extract certain data based on an index and a key value?
I want to return the closest time out of the array, so I wanted to use timeIntervalSince1970 and do some simple math to find the smallest delta. My array contains a key that returns time in milliseconds
Any suggestions on how I can clean my conditional up so I can still extract: lowTideTime - highTideTime and tideType
Below is my method I am using to extract this information:
- (NSArray *)findUpcomingTides: (NSArray *)arrayOfTideCycles {
NSTimeInterval currentDateInterval;
currentDateInterval = [[NSDate date]timeIntervalSince1970];
NSInteger smallestDelta = currentDateInterval;
NSArray *upcomingTideData = [[NSArray alloc] init];
for (NSInteger i = 0; i < arrayOfTideCycles.count; i++) {
NSDictionary *eachTideSummary = [arrayOfTideCycles objectAtIndex:i];
NSInteger tideDateAsEPOCH = [[eachTideSummary valueForKeyPath:#"epoch"] intValue];
NSInteger dateDelta = tideDateAsEPOCH - smallestDelta;
if (dateDelta < smallestDelta) {
smallestDelta = dateDelta;
int iPlusOne = i+1;
upcomingTide = [arrayOfTideCycles objectAtIndex:i];
NSDictionary *tideTypeDictionary = [arrayOfTideCycles objectAtIndex:i];
tideType = [tideTypeDictionary objectForKey:#"type"];
if([[upcomingTide valueForKeyPath:#"type"] isEqualToString:#"Low Tide"] || [[upcomingTide valueForKeyPath:#"type"] isEqualToString:#"Max Ebb"]){
NSString *lowTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
NSDictionary *upcomingHighTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *highTidePrettyDateFormat = [upcomingHighTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
} else {
NSString *highTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
NSDictionary *upcomingLowTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *lowTidePrettyDateFormat = [upcomingLowTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
}
upcomingTideData = [NSArray arrayWithObjects:lowTideTime, highTideTime, tideType, nil];
}
}
return upcomingTideData;
}
Any suggestions on how I can clean this up?
As I understand the question what you dislike is violation of DRY in this parallelism:
NSString *lowTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
NSDictionary *upcomingHighTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *highTidePrettyDateFormat = [upcomingHighTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
and
NSString *highTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
NSDictionary *upcomingLowTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *lowTidePrettyDateFormat = [upcomingLowTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
As far as I can tell at first glance, they are absolutely identical (aside from some unimportant local variable names). So factor them out into a method that takes the upcomingTide value and returns an array of the two tide times (or whatever it is that these two bits of code are supposed to produce for you).
Some suggestions:
valueForKeyPath is a very general and complex method that will run relatively slow; objectForKey will be a lot quicker.
If your array contains data in milliseconds, then your code is in trouble. intValue only handles values between about +/- 2 billion. 2 billion milliseconds = 2 million seconds = a bit over a month. Use longLongValue or doubleValue. (If your data is actually in seconds, your code will go wrong some time around 2038).
I don't know exactly what you are trying to do, but this
NSInteger dateDelta = tideDateAsEPOCH - smallestDelta;
if (dateDelta < smallestDelta) {
smallestDelta = dateDelta;
is wrong. Just step through it with the debugger and see how the values change. And I'm sure you'll want an absolute value taken somewhere in there.
Your code will crash if the last array element gives the smallest delta, because you will access the array element one further which is beyond the array.
If you have say 10,000 array elements, five thousand before and five thousand after the current date, you will find the best element so far five thousand times, and each time you do significant work. I'd first find the best array element, and when that is found, get the data that you want.
Related
I am new to Objective C. What my code currently does is create a new Card object, assigns properties to the object and then adds the Card object to the cards array. The value numberOfCards varies.
for (int i = 0; i < numberOfCards; i++) {
Card *addCard = [Card new];
addCard.balance = [balanceArray objectAtIndex:i];
addCard.date = [dateArray objectAtIndex:i];
addCard.name = [nameArray objectAtIndex:i];
addCard.number = [numberArray objectAtIndex:i];
[cards addObject:addCard];
}
However, what I want to do is give each card a unique name. For example, if numberOfCards was n, then we would get the Card variable names of addCard1, addCard2 ... addCardn.
So how can I append i onto addCard?
Cheers
You mean like this?
NSString *cardName = [[NSString alloc] initWithFormat:#"addCard%i", i + 1];
addCard.name = cardName;
In order to chain a number to a string all you have to do is:
NSString *cardNewName = [NSString stringWithFormat:#"%#%i",cardName,cardNumber];
The idea is to build a format of string and then chain the necessary parameters.
%# is for string
%i is for integer
%f for float
etc.
It's better to use stringWithFormat then [[NSString alloc] initWithFormat:...] - for an extended information please take a look at the following question:
stringWithFormat vs. initWithFormat on NSString
How can I check which value in an NSMutableArray is the most frequent?
Array = "0,2,3,2,2,4
the value = "2".
Take a look at NSCountedSet this will help with your problem.
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:youArray];
NSInteger maxCount = 0;
id maxObject = nil;
for (id object in countedSet) {
if ([countedSet object] > maxCount) {
maxCount = [countedSet countForObject:object];
maxObject = object;
}
}
return maxObject;
This does sound like homework though.
EDIT
If they are stored as strings instead of numbers then swap out NSNumber for NSString everything else works the same.
EDIT
Actually, I just realised that it doesn't care about what object type it is...
Latest edit will work whatever the object is.
I have a method that I call that calculates the Sunrise, Noon and Sunset for any given day. I pass the method the day date as a Julian.
The method need to return the three numbers or strings: Sunrise, Noon and Sunset.
I am trying to call it as follows:
ClassSolarCalculations *LINK = [[ClassSolarCalculations alloc] init];
NSString dateSunrise= [[NSString alloc] initWithFormat:#"%f", [LINK CalculateSunrise: Julian]];
where the Method reads:
(NSDictionary *) CalculateSunrise: (double) Julian;
NSDictionary *returnTimes = [NSDictionary initWithObjectsAndKeys: SunriseText, #"Sunrise", NoonText, "#Noon", SunsetText, #"Sunset", nil];
return returnTimes;
I can this approach to work to return a single value but would like to return all three in one go rather than fudge the solution by calling variants of the routine three times…
Lots of things should be changed here:
method and variable names should start with lowercase letters and use camel case.
Rename your CalculateSunrise: method since it will return more values. Maybe calculateSunTimes:.
Since your method returns an NSDictionary you handling of the return needs to be different.
Try this:
ClassSolarCalculations *link = [[ClassSolarCalculations alloc] init];
NSDictionary *times = [link calculateSunTimes:julian];
NSString *sunrise = times[#"sunrise"];
NSString *noon = times[#"noon"];
NSString *sunset = times[#"sunset"];
Your method would be something like:
- (NSDictionary *)calculateSunTimes:(double)julian {
// calculate the three values:
return #{ #"sunrise" : sunriseText, #"sunset" : sunsetText, #"noon" : noonText };
}
Notice the use of modern Objective-C syntax.
This is my first question to Stack Overflow. I have been using this site for a while and have used it's resources to figure out answers to my programming questions but I'm afraid I can't find the answer I'm looking for this time.
I've created these five strings:
//List five items from the book and turn them into strings
//1 Josh the Trucker
NSString *stringJosh = #"Josh the Trucker";
//2 The Witch from the Remote Town
NSString *stringWitch = #"The Witch from the Remote Town";
//3 Accepting the curse rules "Willingly and Knowingly"
NSString *stringRules = #"Accepting the curse rules, --Willingly and Knowingly--";
//4 Josh's time left to live--Five Days Alive Permitted
NSString *stringFiveDays = #"Josh's time left to live--Five Days Alive Permitted";
//5 The Fire Demon Elelmental
NSString *stringDemon = #"The Fire Demon Elelmental";
Then, I've put them in an array:
//Create an array of five items from the book
NSArray *itemsArray = [[NSArray alloc] initWithObjects:
stringJosh,
stringWitch,
stringRules,
stringFiveDays,
stringDemon,
nil];
Then, I created this mutable string where I need to loop through the array and append the items to a UIlabel.
NSMutableString *itemsString = [[NSMutableString alloc] initWithString:
#"itemsArray"];
Here's the loop, which displays the items in the console log.
for (int i=0; i<5; i++)
{
NSLog(#"Book Item %d=%#", i, itemsArray[i]);
}
My question is, how do I append these items into the UIlabel?
These functions are in my appledelegate.
In my viewDidAppear function (flipsideViewController) I have:
label8.text =""----thats where the looped info needs to go.
How do I do this?
I feel I need to put them together and append where the NSLog should be...but how do I transfer that info to the textlabel?
I hope I explained myself.
We haven't done ANY append examples, I guess this is where I need to get answers from the "wild"
This is the wildest coding environment I know so I'm hoping I can find some direction here.
Thanks for taking a look!
Once you have all your strings that you want to concatenate in NSArray you can combine them with single call (with whatever separator you want):
NSString *combinedString = [itemsArray componentsJoinedByString:#" "];
If you need more complex logic you can use NSMutableString to create result you want while iterating array, i.e.:
NSMutableString *combinedString = [NSMutableString string];
[itemsArray enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) {
[combinedString appendFormat:#"Book Item %d=%# ", idx, obj];
}];
Note also that it is better to iterate through collections using fast enumeration or block enumeration rather than using plain index-based for loop.
NSMutableString *labelText = [NSMutableString string];
int i = 0;
for (NSString *item in itemsArray)
[labelText appendFormat:#"Book Item %d=%#\n", i++, item];
label8.text = labelText;
DO this
UILabel *mainlabel;
mainlabel.text = [origText stringByAppendingString:get];
Add your text to mainlabel.. orig text is mutable string or else in forloop just append array object at index text to label.put above line of code in forloop
Okay I am new to objective C and am trying hard to learn it on my own with out bother the stacked overflow community to much but it is really quite different then what I'm used to (C++).
But I have come across a issue that I for the life of me can't figure out and I'm sure it's going be something stupid. But I am pulling questions and answers from a website that then will display on my iOS application by using this code.
NSString * GetUrl = [NSString stringWithFormat:#"http://www.mywebpage.com/page.php"];
NSString * GetAllHtml = [NSString stringWithContentsOfURL:[NSURL URLWithString:GetUrl] encoding:1 error:nil];
NSString *PullWholeQuestion = [[GetAllHtml componentsSeparatedByString:#"<tr>"] objectAtIndex:1];
NSString *FinishWholeQuestion = [[PullWholeQuestion componentsSeparatedByString:#"</tr>"] objectAtIndex:0];
After I get all of the webpage information I strip down each question and want to make it where it will do a loop process to pull the questions so basically I need to count how many array options there are for the FinishedWholeQuestion variable
I found this snippet online that seemed to work with there example but I cant repeat it
NSArray *stringArray = [NSArray arrayWithObjects:#"1", #"2", nil];
NSLog(#"count = %d", [stringArray count]);
"componentsSeparatedByString" returns an NSArray object, not a single NSString.
An array object can contain zero, one or more NSString objects, depending on the input.
If you change "FinishWholeQuestion" into a NSArray object, you'll likely get a few components (separate by a string).
And now that I'm looking at your code a little more closely, I see you're making an assumption that your array is always valid (and has more than 2 entries, as evidenced by the "objectAtIndex: 1" bit).
You should also change the first character of all your Objective-C variables. Best practices in Objective-C are that the first character of variables should always be lower case.
Like this:
NSString * getUrl = [NSString stringWithFormat:#"http://www.mywebpage.com/page.php"];
NSString * getAllHtml = [NSString stringWithContentsOfURL:[NSURL URLWithString:getUrl] encoding: NSUTF8StringEncoding error:nil];
NSArray * allQuestions = [getAllHtml componentsSeparatedByString:#"<tr>"];
if([allQuestions count] > 1)
{
// assuming there is at least two entries in this array
NSString * pullWholeQuestion = [allQuestions objectAtIndex: 1];
if(pullWholeQuestion)
{
NSString *finishWholeQuestion = [[pullWholeQuestion componentsSeparatedByString:#"</tr>"] objectAtIndex:0];
}
}