Objective C Loop - objective-c

I am trying to loop this code until the yser types in DONE. I used the while loop but only parts of the embedded while loop is executed. Why is the first prompt(Enter the name) not executing in the following program? Thanks
int main (int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int number;
int i = 1;
double payRate, hours, totalPay;
NSString *name;
NSString *amount;
char inputBuffer[200];
NSNumberFormatter *price = [[NSNumberFormatter alloc] init];
[price setNumberStyle:NSNumberFormatterCurrencyStyle];
[price setCurrencySymbol:#"$"];
while(i > 0){
NSLog (#"Enter the name:");
scanf("%[^\n]", inputBuffer);
name = [[NSString alloc] initWithUTF8String:inputBuffer];
if([name isEqualToString:#"DONE"])
break;
else{
NSLog (#"Enter the total number of hours: ");
scanf ("%lf", &hours);
NSLog (#"Enter the pay rate: ");
scanf ("%lf", &payRate);
if(hours <= 40)
totalPay = hours * payRate;
else
totalPay = 400 + (payRate * (hours - 40) * 1.5);
NSString *myString = [NSString stringWithFormat:#"%f",totalPay];
NSNumber *myNumber = [NSNumber numberWithDouble:[myString doubleValue]];
amount = [price stringFromNumber:myNumber];
NSLog(#"Name: %#", name);
NSLog(#"Hours:%.2lf", hours);
NSLog(#"Pay Rate:%.2lf",payRate);
NSLog(#"Total Pay:%#", amount);
NSLog(#"\n");
}
}
NSLog (#"DONE!");
}

Your problem can be contributed to a number of buffering issues, such as scanf()ing numbers, but not the following line feeds. On top of this, I sometimes find that NSLog() messes of stdin/stdout buffering if you are also using printf() or scanf(). Usually I find it best to avoid the scanf() function whenever possible, especially when using a higher-level language like Objective-C.
Instead of using scanf() to read user input, I wrote a small function to read a line from the console, and return it as an NSString. This function looks as follows:
NSString * readLine (FILE * input) {
NSMutableString * string = [[NSMutableString alloc] init];
int aChar = 0;
while ((aChar = fgetc(input)) != EOF) {
if (aChar != '\r') {
if (aChar == '\n') {
break;
} else if (aChar > 0) {
[string appendFormat:#"%C", aChar];
}
}
}
return [string autorelease];
}
Using this, you could rewrite your main() function using this new method. In the following code I have also taken out all NSLog() statements that prompt the user for information, replacing them with more appropriate printf() calls.
int main (int argc, char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
double payRate, hours, totalPay;
NSString * name;
NSString * amount;
NSNumberFormatter * price = [[NSNumberFormatter alloc] init];
[price setNumberStyle:NSNumberFormatterCurrencyStyle];
[price setCurrencySymbol:#"$"];
while (YES) {
printf("Please, enter your name: ");
name = readLine(stdin);
if ([name isEqualToString:#"DONE"])
break;
else {
printf("Enter the total number of hours: ");
hours = [readLine(stdin) intValue];
printf("Enter the pay rate: ");
payRate = [readLine(stdin) intValue];
if (hours <= 40)
totalPay = hours * payRate;
else
totalPay = 400 + (payRate * (hours - 40) * 1.5);
NSNumber * myNumber = [NSNumber numberWithDouble:totalPay];
amount = [price stringFromNumber:myNumber];
NSLog(#"Name: %#", name);
NSLog(#"Hours: %.2lf", hours);
NSLog(#"Pay Rate: %.2lf",payRate);
NSLog(#"Total Pay: %#", amount);
}
}
[price release];
NSLog(#"DONE!");
[pool drain];
}

In: scanf("%[^\n]", inputBuffer); that does not read anything from the user, you need to read from the user into inputBuffer.
Also NSLog is not a good way to send text to the user, probably use "C" functions since you are writing a "C" program.

Related

hackerrank.com find-hackerrank solution on Obj-C

I'm trying to solve https://www.hackerrank.com/challenges/find-hackerrank on Obj-C, and get ok output via xCode, but not via hackerrank's "Run Code" button.
xCode output:
hackerrank output:
So it is at leat strange to see different outputs.
my code:
#import <Foundation/Foundation.h>
int main()
{
NSFileHandle *input;
NSData *inputData;
NSString *match = #"hackerrank";
int amount;
NSString *str;
input = [NSFileHandle fileHandleWithStandardInput];
inputData = [input availableData];
amount = [[[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding] intValue];
for (int j = 0; j < amount; j++)
{
inputData = [input availableData];
str = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
str = [str stringByReplacingOccurrencesOfString:#"\n" withString:#""];
NSArray *redexArr = #[match,
[NSString stringWithFormat:#"^%#.+", match],
[NSString stringWithFormat:#".+%#$", match]
];
for (int i = 2; i>=-1; i--)
{
if (i <= -1)
{
printf("-1\n");
} else
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", redexArr[i]];
if ([pred evaluateWithObject:str])
{
printf("%d\n", i);
break;
}
}
}
}
return 0;
}
Any ideas?
The difference between the run in Xcode and on HackerRank is that when you run locally, the call to
[input availableData];
stops when your program have read the next line from console. This lets you call availableData multiple times, each time getting the next line.
On HackerRank, though the very first call of availableData gets the entire file, leaving nothing to the rest of your program to consume.
You can fix this problem by reading the file line-by-line, or reading the entire content, and splitting it on end-of-line markers.
Here is your fixed submission that passes all tests on HackerRank:
#import <Foundation/Foundation.h>
int main()
{
NSFileHandle *input;
NSString *match = #"hackerrank";
int amount;
input = [NSFileHandle fileHandleWithStandardInput];
NSArray *inputData = [[[NSString alloc] initWithData:[input availableData] encoding:NSUTF8StringEncoding] componentsSeparatedByString: #"\n"];
amount = [inputData[0] intValue];
for (int j = 1; j <= amount; j++)
{
NSString *str = inputData[j];
NSArray *redexArr = #[match,
[NSString stringWithFormat:#"^%#.+", match],
[NSString stringWithFormat:#".+%#$", match]
];
for (int i = 2; i>=-1; i--)
{
if (i <= -1)
{
printf("-1\n");
} else
{
NSPredicate *pred = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", redexArr[i]];
if ([pred evaluateWithObject:str])
{
printf("%d\n", i);
break;
}
}
}
}
return 0;
}

Separate digits in an NSString to display a phone number

I have an NSString that holds something like this:
4434332124
How can I make that into something like this?
443-433-2124
This can be done in only two lines just use NSString's stringWithFormat method and chop up the phone number into individual substrings and glue the whole thing together in your format string. Something like this:
NSString *sPhone = #"4434332124";
NSString *formatted = [NSString stringWithFormat: #"%#-%#-%#", [sPhone substringWithRange:NSMakeRange(A,B)],[sPhone substringWithRange:NSMakeRange(B,C)],
[sPhone substringWithRange:NSMakeRange(C,D)]];
EDIT: Working Code
NSString *formatted = [NSString stringWithFormat: #"%#-%#-%#", [sPhone substringWithRange:NSMakeRange(0,3)],[sPhone substringWithRange:NSMakeRange(3,3)],
[sPhone substringWithRange:NSMakeRange(6,4)]];
If you want to just do a simple quick replacement on what you are sure is a 10 digit number then try this regex example (iOS >= 3.2).
NSString *tenDigitNumber = #"5554449999";
tenDigitNumber = [tenDigitNumber stringByReplacingOccurrencesOfString:#"(\\d{3})(\\d{3})(\\d{4})"
withString:#"$1-$2-$3"
options:NSRegularExpressionSearch
range:NSMakeRange(0, [tenDigitNumber length])];
NSLog(#"%#", tenDigitNumber);
Not as pretty as trying to do something with NSFormatter (though unless you subclass it I didn't see an obvious way to get it to do something like phone numbers) but you could do this:
NSString* newString = [NSString stringWithFormat:#"%c%c%c-%c%c%c-%c%c%c%c" [phoneNumber characterAtIndex:0],
[phoneNumber characterAtIndex:1],
[phoneNumber characterAtIndex:2],
[phoneNumber characterAtIndex:3],
[phoneNumber characterAtIndex:4],
[phoneNumber characterAtIndex:5],
[phoneNumber characterAtIndex:6],
[phoneNumber characterAtIndex:7],
[phoneNumber characterAtIndex:8],
[phoneNumber characterAtIndex:9]];
#import <Foundation/Foundation.h>
static NSString * const theInString = #"4434332124";
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
const char * theUniChar = theInString.UTF8String;
NSMurableString * theResult = nil;
for( int i = 0, c = (int)strlen(theUniChar); i < c; i+=2 )
{
char thePart[4];
sscanf( theUniChar + i, "%3s", &thePart );
if( theResult == nil )
theResult = [NSMutableString string];
else
[theResult appendFormate:#"-%3s",thePart];
}
printf( "%#\n", theResult );
[pool drain];
return 0;
}

Reverse NSString text

I have been googling so much on how to do this, but how would I reverse a NSString? Ex:hi would become: ih
I am looking for the easiest way to do this.
Thanks!
#Vince I made this method:
- (IBAction)doneKeyboard {
// first retrieve the text of textField1
NSString *myString = field1.text;
NSMutableString *reversedString = [NSMutableString string];
NSUInteger charIndex = 0;
while(myString && charIndex < [myString length]) {
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
charIndex++;
}
// reversedString is reversed, or empty if myString was nil
field2.text = reversedString;
}
I hooked up that method to textfield1's didendonexit. When I click the done button, it doesn't reverse the text, the UILabel just shows the UITextField's text that I entered. What is wrong?
Block version.
NSString *myString = #"abcdefghijklmnopqrstuvwxyz";
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[myString length]];
[myString enumerateSubstringsInRange:NSMakeRange(0,[myString length])
options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
// reversedString is now zyxwvutsrqponmlkjihgfedcba
Write a simple loop to do that:
// myString is "hi"
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
}
NSLog(#"%#", reversedString); // outputs "ih"
In your case:
// first retrieve the text of textField1
NSString *myString = textField1.text;
NSMutableString *reversedString = [NSMutableString string];
NSInteger charIndex = [myString length];
while (myString && charIndex > 0) {
charIndex--;
NSRange subStrRange = NSMakeRange(charIndex, 1);
[reversedString appendString:[myString substringWithRange:subStrRange]];
}
// reversedString is reversed, or empty if myString was nil
textField2.text = reversedString;
jano’s answer is correct. Unfortunately, it creates a lot of unnecessary temporary objects. Here is a much faster (more complicated) implementation that basically does the same thing, but uses memcpy and unichar buffers to keep memory allocations to a minimum.
- (NSString *)reversedString
{
NSUInteger length = [self length];
if (length < 2) {
return self;
}
unichar *characters = calloc(length, sizeof(unichar));
unichar *reversedCharacters = calloc(length, sizeof(unichar));
if (!characters || !reversedCharacters) {
free(characters);
free(reversedCharacters);
return nil;
}
[self getCharacters:characters range:NSMakeRange(0, length)];
NSUInteger i = length - 1;
NSUInteger copiedCharacterCount = 0;
// Starting from the end of self, copy each composed character sequence into reversedCharacters
while (copiedCharacterCount < length) {
NSRange characterRange = [self rangeOfComposedCharacterSequenceAtIndex:i];
memcpy(reversedCharacters + copiedCharacterCount, characters + characterRange.location, characterRange.length * sizeof(unichar));
i = characterRange.location - 1;
copiedCharacterCount += characterRange.length;
}
free(characters);
NSString *reversedString = [[NSString alloc] initWithCharactersNoCopy:reversedCharacters length:length freeWhenDone:YES];
if (!reversedString) {
free(reversedCharacters);
}
return reversedString;
}
I tested this on 100,000 random multi-byte Unicode strings with lengths between 1 and 128. This version is about 4–5x faster than jano’s.
Enumerate substrings: 2.890528
MemCopy: 0.671090
Enumerate substrings: 2.840411
MemCopy: 0.662882
Test code is at https://gist.github.com/prachigauriar/9739805.
Update: I tried this again by simply converting to a UTF-32 buffer and reversing that.
- (NSString *)qlc_reversedStringWithUTF32Buffer
{
NSUInteger length = [self length];
if (length < 2) {
return self;
}
NSStringEncoding encoding = NSHostByteOrder() == NS_BigEndian ? NSUTF32BigEndianStringEncoding : NSUTF32LittleEndianStringEncoding;
NSUInteger utf32ByteCount = [self lengthOfBytesUsingEncoding:encoding];
uint32_t *characters = malloc(utf32ByteCount);
if (!characters) {
return nil;
}
[self getBytes:characters maxLength:utf32ByteCount usedLength:NULL encoding:encoding options:0 range:NSMakeRange(0, length) remainingRange:NULL];
NSUInteger utf32Length = utf32ByteCount / sizeof(uint32_t);
NSUInteger halfwayPoint = utf32Length / 2;
for (NSUInteger i = 0; i < halfwayPoint; ++i) {
uint32_t character = characters[utf32Length - i - 1];
characters[utf32Length - i - 1] = characters[i];
characters[i] = character;
}
return [[NSString alloc] initWithBytesNoCopy:characters length:utf32ByteCount encoding:encoding freeWhenDone:YES];
}
This is about 3–4x times faster than the memcpy version. The aforementioned gist has been updated with the latest version of the code.
Enumerate substrings: 2.168705
MemCopy: 0.488320
UTF-32: 0.150822
Enumerate substrings: 2.169655
MemCopy: 0.481786
UTF-32: 0.147534
Enumerate substrings: 2.248812
MemCopy: 0.505995
UTF-32: 0.154531
I thought I'd throw another version out there in case anyone's interested.. personally, I like the cleaner approach using NSMutableString but if performance is the highest priority this one is faster:
- (NSString *)reverseString:(NSString *)input {
NSUInteger len = [input length];
unichar *buffer = malloc(len * sizeof(unichar));
if (buffer == nil) return nil; // error!
[input getCharacters:buffer];
// reverse string; only need to loop through first half
for (NSUInteger stPos=0, endPos=len-1; stPos < len/2; stPos++, endPos--) {
unichar temp = buffer[stPos];
buffer[stPos] = buffer[endPos];
buffer[endPos] = temp;
}
return [[NSString alloc] initWithCharactersNoCopy:buffer length:len freeWhenDone:YES];
}
I also wrote a quick test as well to compare this with the more traditional NSMutableString method (which I also included below):
// test reversing a really large string
NSMutableString *string = [NSMutableString new];
for (int i = 0; i < 10000000; i++) {
int digit = i % 10;
[string appendFormat:#"%d", digit];
}
NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
NSString *reverse = [self reverseString:string];
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSince1970] - startTime;
NSLog(#"reversed in %f secs", elapsedTime);
Results were:
using NSMutableString method (below) - "reversed in 3.720631 secs"
using unichar *buffer method (above) - "reversed in 0.032604 secs"
Just for reference, here's the NSMutableString method used for this comparison:
- (NSString *)reverseString:(NSString *)input {
NSUInteger len = [input length];
NSMutableString *result = [[NSMutableString alloc] initWithCapacity:len];
for (int i = len - 1; i >= 0; i--) {
[result appendFormat:#"%c", [input characterAtIndex:i]];
}
return result;
}
Use method with any objects: NSString,NSNumber,etc..:
NSLog(#"%#",[self reverseObject:#12345]);
NSLog(#"%#",[self reverseObject:#"Hello World"]);
Method:
-(NSString*)reverseObject:(id)string{
string = [NSString stringWithFormat:#"%#",string];
NSMutableString *endString = [NSMutableString new];
while ([string length]!=[endString length]) {
NSRange range = NSMakeRange([string length]-[endString length]-1, 1);
[endString appendString: [string substringWithRange:range]];
}
return endString;}
Log:
2014-04-16 11:20:25.312 TEST[23733:60b] 54321
2014-04-16 11:20:25.313 TEST[23733:60b] dlroW olleH
Swift 2.0:
1) let str = "Hello, world!"
let reversed = String(str.characters.reverse())
print(reversed)
In Short:
String("This is a test string.".characters.reverse())
2)
let string = "This is a test string."
let characters = string.characters
let reversedCharacters = characters.reverse()
let reversedString = String(reversedCharacters)
The short way :
String("This is a test string.".characters.reverse())
OR
let string = "This is a test string."
let array = Array(string)
let reversedArray = array.reverse()
let reversedString = String(reversedArray)
The short way :
String(Array("This is a test string.").reverse())
Tested on Play Ground:
import Cocoa
//Assigning a value to a String variable
var str = "Hello, playground"
//Create empty character Array.
var strArray:Character[] = Character[]()
//Loop through each character in the String
for character in str {
//Insert the character in the Array variable.
strArray.append(character)
}
//Create a empty string
var reversedStr:String = ""
//Read the array from backwards to get the characters
for var index = strArray.count - 1; index >= 0;--index {
//Concatenate character to String.
reversedStr += strArray[index]
}
The shorter version:
var str = “Hello, playground”
var reverseStr = “”
for character in str {
reverseStr = character + reverseStr
}
Would it be faster if you only iterated over half the string swapping the characters at each end? So for a 5 character string, you swap characters 1 + 5, then 2 + 4 and 3 doesn't need swapped with anything.
NSMutableString *reversed = [original mutableCopyWithZone:NULL];
NSUInteger i, length;
length = [reversed length];
for (i = 0; i < length / 2; i++) {
// Store the first character as we're going to replace with the character at the end
// in the example, it would store 'h'
unichar startChar = [reversed characterAtIndex:i];
// Only make the end range once
NSRange endRange = NSMakeRange(length - i, 1);
// Replace the first character ('h') with the last character ('i')
// so reversed now contains "ii"
[reversed replaceCharactersInRange:NSMakeRange(i, 1)
withString:[reversed subStringWithRange:endRange];
// Replace the last character ('i') with the stored first character ('h)
// so reversed now contains "ih"
[reversed replaceCharactersInRange:endRange
withString:[NSString stringWithFormat:#"%c", startChar]];
}
edit ----
Having done some tests, the answer is No, its about 6 times slower than the version that loops over everything. The thing that slows us down is creating the temporary NSStrings for the replaceCharactersInRange:withString method. Here is a method that creates only one NSString by manipulating the character data directly and seems a lot faster in simple tests.
NSUInteger length = [string length];
unichar *data = malloc(sizeof (unichar) * length);
int i;
for (i = 0; i < length / 2; i++) {
unichar startChar = [string characterAtIndex:i];
unichar endChar = [string characterAtIndex:(length - 1) - i];
data[i] = endChar;
data[(length - 1) - i] = startChar;
}
NSString *reversed = [NSString stringWithCharacters:data length:length];
free(data);
Reverse the string using recursion:
#implementation NSString (Reversed)
+ (NSString *)reversedStringFromString:(NSString *)string
{
NSUInteger count = [string length];
if (count <= 1) { // Base Case
return string;
} else {
NSString *lastLetter = [string substringWithRange:NSMakeRange(count - 1, 1)];
NSString *butLastLetter = [string substringToIndex:count - 1];
return [lastLetter stringByAppendingString:[self reversedStringFromString:butLastLetter]];
}
}
#end
Google is your friend:
-(NSString *) reverseString
{
NSMutableString *reversedStr;
int len = [self length];
// Auto released string
reversedStr = [NSMutableString stringWithCapacity:len];
// Probably woefully inefficient...
while (len > 0)
[reversedStr appendString:
[NSString stringWithFormat:#"%C", [self characterAtIndex:--len]]];
return reversedStr;
}
None of the answers seem to consider multibyte characters so here is my sample code. It assumes you only ever pass in a string longer than one character.
- (void)testReverseString:(NSString *)string
{
NSMutableString *rString = [NSMutableString new];
NSInteger extractChar = [string length] - 1;
while (extractChar >= 0)
{
NSRange oneCharPos = [string rangeOfComposedCharacterSequenceAtIndex:extractChar];
for (NSUInteger add = 0; add < oneCharPos.length; ++ add)
{
unichar oneChar = [string characterAtIndex:oneCharPos.location + add];
[rString appendFormat:#"%C", oneChar];
}
extractChar -= oneCharPos.length;
}
NSLog(#"%# becomes %#", string, encryptedString );
}
NSString into char utf32 (always 32 bits (unsigned int))
Reverse
char utf32 into NSString
+ (NSString *)reverseString3:(NSString *)str {
unsigned int *cstr, buf, len = [str length], i;
cstr = (unsigned int *)[str cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
for (i=0;i < len/2;i++) buf = cstr[i], cstr[i] = cstr[len -i-1], cstr[len-i-1] = buf;
return [[NSString alloc] initWithBytesNoCopy:cstr length:len*4 encoding:NSUTF32LittleEndianStringEncoding freeWhenDone:NO];
}
Example : Apple_is  --->  si_elppA
NSMutableString *result = [NSMutableString stringWithString:#""];
for (long i = self.length - 1; i >= 0; i--) {
[result appendFormat:#"%c", [self characterAtIndex:i]];
}
return (NSString *)result;
Here is a collection of categories in Objective-C that will reverse both NSStrings and NSAttributedStrings (while preserving character attributes): TextFlipKit
For example:
NSString *example = #"Example Text";
NSString *reversed = example.tfk_reversed;
NSLog(#"Reversed: %#", reversed);
//prints 'Reversed: txeT elpmaxE'
Swift:
let string = "reverse"
let reversedStringCollection = string.characters.reversed()
for character in reversedStringCollection {
reversedString.append(character)
print(reversedString)
}
We can also achieve the reverse string as follows.
NSString *originalString = #"Hello";
NSString *reverseString;
for (NSUInteger index = originalString.length; index > 0; index--) {
char character = [originalString characterAtIndex:index];
reverseString = [reverseString stringByAppendingString:[NSString stringWithFormat:#"%c", character]];
}
or
NSString *originalString = #"Hello";
NSString *reverseString;
for (NSUInteger index = originalString.length; index > 0; index--) {
char *character = [originalString characterAtIndex:index];
reverseString = [reverseString stringByAppendingString:[NSString stringWithFormat:#"%s", character]];
}
Add a category to NSString so you can call reverse on any NSString in the future like this:
#import "NSString+Reverse.h"
#implementation NSString (Reverse)
-(NSString*)reverse {
char* cstring = (char*)[self UTF8String];
int length = [self length]-1;
int i=0;
while (i<=length) {
unichar tmp = cstring[i];
cstring[i] = cstring[length];
cstring[length] = tmp;
i++;
length--;
}
return [NSString stringWithCString:cstring encoding:NSUTF8StringEncoding];
}
#end
str=#"india is my countery";
array1=[[NSMutableArray alloc] init];
for(int i =0 ;i<[str length]; i++) {
NSString *singleCharacter = [NSString stringWithFormat:#"%c", [str characterAtIndex:i]];
[array1 addObject:singleCharacter];
}
NSMutableString* theString = [NSMutableString string];
for (int i=[array1 count]-1; i>=0;i--){
[theString appendFormat:#"%#",[array1 objectAtIndex:i]];
}
I have written a category ove that one :D
//NSString+Reversed.h
#import
//
// NSString+Reversed.h
// HTMLPageFormatter
// Created by beit46 on 21.06.13.
//
#interface NSString (Reversed)
- (NSString *)reversedString;
#end
//NSString+Reversed.m
//
// NSString+Reversed.m
// HTMLPageFormatter
// Created by beit46 on 21.06.13.
#import "NSString+Reversed.h"
#implementation NSString (Reversed)
- (NSString *)reversedString {
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[self length]];
[self enumerateSubstringsInRange:NSMakeRange(0,[self length])
options:(NSStringEnumerationReverse | NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
return [reversedString copy];
}
#end
I have two simple solutions for that purpose:
+(NSString*)reverseString:(NSString *)str
{
NSMutableString* reversed = [NSMutableString stringWithCapacity:str.length];
for (int i = (int)str.length-1; i >= 0; i--){
[reversed appendFormat:#"%c", [str characterAtIndex:i]];
}
return reversed;
}
+(NSString*)reverseString2:(NSString *)str
{
char* cstr = (char*)[str UTF8String];
int len = (int)str.length;
for (int i = 0; i < len/2; i++) {
char buf = cstr[i];
cstr[i] = cstr[len-i-1];
cstr[len-i-1] = buf;
}
return [[NSString alloc] initWithBytes:cstr length:len encoding:NSUTF8StringEncoding];
}
Now, lets test it!
NSString* str = #"Objective-C is a general-purpose, object-oriented programming language that adds Smalltalk-style messaging to the C programming language";
NSLog(#"REV 1: %#", [Util reverseString:str]);
start = [NSDate date];
for (int i = 0 ; i < 1000; ++i)
[Util reverseString:str];
end = [NSDate date];
NSLog(#"Time per 1000 repeats: %f", [end timeIntervalSinceDate:start]);
NSLog(#"REV 2: %#", [Util reverseString2:str]);
start = [NSDate date];
for (int i = 0 ; i < 1000; ++i)
[Util reverseString2:str];
end = [NSDate date];
NSLog(#"Time per 1000 repeats: %f", [end timeIntervalSinceDate:start]);
Results:
ConsoleTestProject[68292:303] REV 1: egaugnal gnimmargorp C eht ot gnigassem elyts-klatllamS sdda taht egaugnal gnimmargorp detneiro-tcejbo ,esoprup-lareneg a si C-evitcejbO
ConsoleTestProject[68292:303] Time per 1000 repeats: 0.063880
ConsoleTestProject[68292:303] REV 2: egaugnal gnimmargorp C eht ot gnigassem elyts-klatllamS sdda taht egaugnal gnimmargorp detneiro-tcejbo ,esoprup-lareneg a si C-evitcejbO
ConsoleTestProject[68292:303] Time per 1000 repeats: 0.002038
And more chars result was:
ConsoleTestProject[68322:303] chars: 1982
ConsoleTestProject[68322:303] Time 1 per 1000 repeats: 1.014893
ConsoleTestProject[68322:303] Time 2 per 1000 repeats: 0.024928
The same text with above functions:
ConsoleTestProject[68366:303] Time 1 per 1000 repeats: 0.873574
ConsoleTestProject[68366:303] Time 2 per 1000 repeats: 0.019300
ConsoleTestProject[68366:303] Time 3 per 1000 repeats: 0.342735 <-Vladimir Gritsenko
ConsoleTestProject[68366:303] Time 4 per 1000 repeats: 0.584012 <- Jano
So, choose performance!

Convert basic script to Objective C (money formatting)

I've got this basic like script that I need to convert to objective c, it turns big units of money into shortened versions (ie: 1.2m, etc), I've got most of the conversion done, but the biggest problem I'm having is right at the end.
The original basic code is:
; Basic Code
Function ShortCash$(BigNumber)
out$=""
; First, grab the length of the number
L=Len(BigNumber)
Letter$=""
;Next, Do a sweep of the values, and cut them down.
If l<13
out$=(BigNumber/1000000000)
; For each figure, out remainder should be divided so that it leaves a 2 digit decimal number..
remainder=(BigNumber Mod 1000000000)/10000000
; And we also want a letter to symbolise our large amounts..
Letter$="b" ; BILLION!!!!
EndIf
If l<10 Then out$=(BigNumber/1000000):remainder=(BigNumber Mod 1000000)/10000:Letter$="m"
If l<7 Then out$=(BigNumber/1000):remainder=(BigNumber Mod 1000)/10:Letter$="k"
If l<4 Then out$=BigNumber:remainder=0:Letter$=""
;Next, if remainder=0 then we're happy.. ie, £1m is fine, we need no decimal.
;But, if the remainder is >0 we'll want a nice rounded 2 decimal number, instead.
If remainder>0
out$=out$+"."+Right$("00"+remainder,2) ; Last two numbers..
; Additionally, if the rightmost figure is a 0, remove it.
; (ie, if the value is 1.50, we don't need the 0)
If Right$(out$,1)="0" Then out$=Left$(out$,Len(out$)-1)
EndIf
; And throw on our letter, at the end.
out$=out$+letter$
Return out$
End Function
// The following was edited on Thur 5 Aug by Author of post.
I believe I've got it sorted now, I've got the following to work for thousands for the moment, I'm not sure if it will work under all circumstances and would welcome any help/guidance on this. I am aware of the memory issues, I'll sort that out later, its the string manipulation part I am resolving first.
// This goes inside the (IBAction) update method;
NSNumber *bigNumber = nil;
if ( [inputField.text length] >0)
{
bigNumber = [NSNumber numberWithInt:[inputField.text intValue]];
}
int bigNumberAsInt = [bigNumber intValue];
NSString *bigNumberAsString = [bigNumber stringValue];
int bigNumberStrLen = [bigNumberAsString length];
NSLog(#"bigNumber = %#", bigNumber);
//NSLog(#"bigNumberAsString = %#", bigNumberAsString);
NSLog(#"bigNumberStrLen = %d", bigNumberStrLen);
NSLog(#"=========");
// =========
NSNumberFormatter *nformat = [[[NSNumberFormatter alloc] init] autorelease];
[nformat setFormatterBehavior:NSNumberFormatterBehavior10_4];
[nformat setCurrencySymbol:#"$"];
[nformat setNumberStyle:NSNumberFormatterCurrencyStyle];
[nformat setMaximumFractionDigits:0];
NSLog(#"Cash = %#", [nformat stringFromNumber:bigNumber]);
// =========
NSString *output = [[NSString alloc] init];
NSString *letter;
// ==========
// Anything less than 1m represent with a k
if (bigNumberStrLen < 7)
{
letter = #"k";
int sum = (bigNumberAsInt / 1000);
int int_remainder = ((bigNumberAsInt % 1000) / 10);
NSLog(#"Remainder = %d", int_remainder);
NSString *sumAsString = [NSString stringWithFormat:#"%d", sum];
NSString *remainderAsString = [NSString stringWithFormat:#"%d", int_remainder];
NSLog(#"Sum as String = %#", sumAsString);
NSLog(#"Remainder as String = %#", remainderAsString);
if (int_remainder >0)
{
NSLog(#"Remainder > 0");
output = [output stringByAppendingString:sumAsString];
output = [output stringByAppendingString:#"."];
output = [output stringByAppendingString:remainderAsString];
NSLog(#"Output = %#", output);
NSUInteger outputStrLen = [output length];
NSLog(#"Output strlen = %d", outputStrLen);
if ([output hasSuffix:#"0"])
{
NSLog(#"Has suffix of 0");
// Remove suffix
output = [output substringWithRange: NSMakeRange(0, outputStrLen-1)];
}
}
output = [output stringByAppendingString:letter];
NSLog(#"Final output = %#", output);
}
This will display 10.2k (if it ends with a 0 suffix) or it will display 10.2x where X is the last number.
Can someone just double check this, or perhaps there's an easier way to do all this. In either case, thanks for your help.
Just to improve the solution, a good idea is maybe to subclass the NSNumberFormatter class and override the - (NSString *)stringForObjectValue:(id)anObject method.
Using the code from zardon, I added a statement for the values < 1000 which doesn't format the number.
Here is the code of the method :
/*
Override the stringForObjectValue method from NSNumberFormatter
100 -> 100
1000 -> 1k
1 000 000 -> 1m
1 000 000 000 -> 1b
1 000 000 000 -> 1t
*/
- (NSString *)stringForObjectValue:(id)anObject {
// If we don't get a NSNumber, we can't create the string
if (![anObject isKindOfClass:[NSNumber class]]) {
return nil;
}
NSNumberFormatter *nformat = [[NSNumberFormatter alloc] init];
// Decimal value from the NSObject
double doubleValue = [anObject doubleValue];
NSString *stringValue = nil;
// Abbrevations used
NSArray *abbrevations = [NSArray arrayWithObjects:#"k", #"m", #"b", #"t", nil] ;
// If the value is less than 1000, we display directly the value
if(doubleValue < 1000.0) {
stringValue = [NSString stringWithFormat: #"%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] ];
}
else { // Otherwise we format it as expected
for (NSString *s in abbrevations) {
doubleValue /= 1000.0 ;
if ( doubleValue < 1000.0 ) {
if ( (long long)doubleValue % (long long) 100 == 0 ) {
[nformat setMaximumFractionDigits:0];
} else {
[nformat setMaximumFractionDigits:2];
}
stringValue = [NSString stringWithFormat: #"%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] ];
NSUInteger stringLen = [stringValue length];
if ( [stringValue hasSuffix:#".00"] )
{
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-3)];
} else if ( [stringValue hasSuffix:#".0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-2)];
} else if ( [stringValue hasSuffix:#"0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-1)];
}
// Add the letter suffix at the end of it
stringValue = [stringValue stringByAppendingString: s];
break;
}
}
}
[nformat release];
return stringValue;
}
In the interface we simply add the inheritage statement :
#interface MoneyNumberFormatter : NSNumberFormatter
Hope this helps..
.... Okay, with thanks to the author of the Cocoa Tidbits blog, I believe I have a solution which is much more elegant, faster and doesn't require so much coding; it still needs testing, and it also probably requires a little more editing, but it seems to be much better than my original.
I modified the script a little to make it not show any trailing zeros where relevant;
NSNumberFormatter *nformat = [[NSNumberFormatter alloc] init];
[nformat setFormatterBehavior:NSNumberFormatterBehavior10_4];
[nformat setCurrencySymbol:#"$"];
[nformat setNumberStyle:NSNumberFormatterCurrencyStyle];
double doubleValue = 10200;
NSString *stringValue = nil;
NSArray *abbrevations = [NSArray arrayWithObjects:#"k", #"m", #"b", #"t", nil] ;
for (NSString *s in abbrevations)
{
doubleValue /= 1000.0 ;
if ( doubleValue < 1000.0 )
{
if ( (long long)doubleValue % (long long) 100 == 0 ) {
[nformat setMaximumFractionDigits:0];
} else {
[nformat setMaximumFractionDigits:2];
}
stringValue = [NSString stringWithFormat: #"%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] ];
NSUInteger stringLen = [stringValue length];
if ( [stringValue hasSuffix:#".00"] )
{
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-3)];
} else if ( [stringValue hasSuffix:#".0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-2)];
} else if ( [stringValue hasSuffix:#"0"] ) {
// Remove suffix
stringValue = [stringValue substringWithRange: NSMakeRange(0, stringLen-1)];
}
// Add the letter suffix at the end of it
stringValue = [stringValue stringByAppendingString: s];
//stringValue = [NSString stringWithFormat: #"%#%#", [nformat stringFromNumber: [NSNumber numberWithDouble: doubleValue]] , s] ;
break ;
}
}
NSLog(#"Cash = %#", stringValue);

conversion to doubleValue doesn't work .. but to integerValue does

Hello all I'm trying to convert a string to a double and it doesn't work.
However if I convert it to an integer it does work
this is my code snippet
int myDuration = [myDurationString integerValue];
int conversionMinute = myDuration / 60;
if( myDuration < 60 )
{
[appDelegate.rep1 addObject:[NSString stringWithFormat:#"%d",myDuration]];
NSLog(#"show numbers %d", myDuration);
}
else
{
[appDelegate.rep1 addObject:[NSString stringWithFormat:#"%d",conversionMinute]];
NSLog(#"show numbers %d", conversionMinute);
}
Now if I try to do
double myDuration = [myDurationString doubleValue];
double conversionMinute = myDuration / 60;
then it doesn't work. It gives me an output of 0.
So the integerconversion works but somehow the double doesn't does anybody have an idea why?
You need to supply a matching format specifier. Replace every occurence of %d with %f.
%d simply fetches the next 32-bit word from the stack and treats it as a signed integer. Because of the internal representation of the floating point number, this word is zero in quite a few cases, which is why you get 0.
Here is an example that works fine for me (also with other contents of myDurationString):
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * myDurationString = #"19";
double myDuration = [myDurationString doubleValue];
double conversionMinute = myDuration / 60;
if( myDuration < 60 )
{
NSLog(#"show numbers %f", myDuration);
}
else
{
NSLog(#"show numbers %f", conversionMinute);
}
[pool drain];
return 0;
}