parsing string into different kind of number string - objective-c

I have a string called realEstateWorth with a value of $12,000,000.
I need this same string to remain a string but for any number (such as the one above) to be displayed as $12 MILLION or $6 MILLION. The point is it needs the words "MILLION" to come after the number.
I know there is nsNumberFormatter that can convert strings into numbers and vice versa but can it do what I need?
If anyone has any ideas or suggestions, it would be much appreciated.
Thank you!

So as I see it, you have two problems:
You have a string representation of something that's actually a number
You (potentially) have a number that you want formatted as a string
So, problem #1:
To convert a string into a number, you use an NSNumberFormatter. You've got a pretty simple case:
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *n = [f numberFromString:#"$12,000,000"];
// n is 12000000
That was easy! Now problem #2:
This is trickier, because you want a mixed spell-out style. You could consider using an NSNumberFormatter again, but it's not quite right:
[f setNumberStyle:NSNumberFormatterSpellOutStyle];
NSString *s = [f stringFromNumber:n];
// s is "twelve million"
So, we're closer. At this point, you could perhaps maybe do something like:
NSInteger numberOfMillions = [n integerValue] / 1000000;
if (numberOfMillions > 0) {
NSNumber *millions = [NSNumber numberWithInteger:numberOfMillions];
NSString *numberOfMillionsString = [f stringFromNumber:millions]; // "twelve"
[f setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *formattedMillions = [f stringFromNumber:millions]; // "$12.00"
if ([s hasPrefix:numberOfMillionsString]) {
// replace "twelve" with "$12.00"
s = [s stringByReplacingCharactersInRange:NSMakeRange(0, [numberOfMillionsString length]) withString:formattedMillions];
// if this all works, s should be "$12.00 million"
// you can use the -setMaximumFractionDigits: method on NSNumberFormatter to fiddle with the ".00" bit
}
}
However
I don't know how well this would work in anything other than english. CAVEAT IMPLEMENTOR

Worst case scenario, you could implement a category on NSString to implement the behaviour you want.
In the method that you would do in that category you could take an NSNumberFormatter to bring that string to a number and by doing some modulo operation you could define if you need the word Million, or Billion, etc. and put back a string with the modulo for Million or other way you need it to be.
That way you could just call that method on your NSString like this :
NSString *humanReadable = [realEstateWorth myCustomMethodFromMyCategory];
And also.
NSString are immutable, so you can't change it unless you assign a new one to your variable.

I'd recommend storing this value as an NSNumber or a float. Then you could have a method to generate an NSString to display it like:
- (NSString*)numberToCurrencyString:(float)num
{
NSString *postfix = #"";
if (num > 1000000000)
{
num = num / 1000000000;
postfix = #" Billion";
}
else if (num > 1000000)
{
num = num / 1000000;
postfix = #" Million";
}
NSString *currencyString = [NSString stringWithFormat:#"%.0f%#", num, postfix];
return currencyString;
}
Note: Your question states that your input needs to remain a string. That's fine. So you'd need to 1.) first parse the number out of the string and 2.) then reconvert it to a string from a number. I've shown how to do step 2 of this process.

Related

Convert back localized NSString number (> 4 digits) to integer

I used the localizedStringWithFormat: method on NSString class to convert a seven digit integer number to an NSString somewhere in my code and need to convert it back to an integer now.
As my App is localized for different regions with different separators after three digits (e.g. '.' in the U.S. and ',' in Germany), what's the best way to convert a localized NSString integer value to an integer?
I tried integerValue on my string as follows but it didn't work:
// Somewhere in code:
int num = 1049000;
NSString *myLocalizedNumString = [NSString localizedStringWithFormat:#"%d", num];
// myLocalizedNumString (U.S.): '1,049,000'
// myLocalizedNumString (Germany): '1.049.000'
// Somewhere else where I have a reference to my string but none to the num:
int restoredNum = [myLocalizedNumString integerValue];
// restoredNum isn't 1049000 (it's 0, the initial value)
What would be a good working way of doing it?
Despite its name NSNumberFormatter converts both ways, it is also a string parser. Using the method numberFromString after setting the number formatter’s numberStyle property to NSNumberFormatterDecimalStyle solves your problem.
The code might look as follows:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSInteger restoredNum = [[formatter numberFromString:myLocalizedNumString] integerValue];

How do I force a sign-character on the output of an NSNumberFormatter

I want to use a number formatter to generate my output, so the number is automatically formatted for the user's locale, but I want it to work like "%+.1f" does in printf(), that is always have a sign specified.
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
double val = 3.1234;
label.text = [NSString stringWithFormat: #"XXX %# XXX", [nf stringFromNumber: [NSNumber numberWithDouble: val]]];
I want the label to come out "XXX +3.1 XXX" in the US and the appropriate but equivalent string for any other location. The only things I can find are setPositiveFormat: and setPositivePrefix:.
But I don't want to set the format since I don't know how to format numbers in other countries; I don't know if a plus-sign is used to designate a positive number in Arabic or Russian or some culture I have not thought of. I do know, for example, that decimal points, commas, spaces, etc., all have different meanings in European countries compared to the U.S. - Could the same be true for +/- signs?
What I do currently is:
label.text = [NSString stringWithFormat: #"XXX %s%# XXX", (val < 0) ? "" : "+",
[nf stringFromNumber: [NSNumber numberWithDouble: val]]];
But this presumes that '+' and '-' are correct for all formats.
I'm sure it must be there since it is a standard formatting thing that has been in printf() since the dark ages...
How about this:
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
double val = 3.1234;
NSString *sign = (val < 0) ? [nf minusSign] : [nf plusSign];
NSString *num = [nf stringFromNumber:#(abs(val))]; // avoid double negative
label.text = [NSString stringWithFormat: #"XXX %#%# XXX", sign, num];
You may need to check to see if num has the sign prefix or not so it isn't shown twice.
Edit: After some playing around, it has been determined, for the "Decimal" style, that no current locale uses a positivePrefix. No current locale uses a plusSign other than the standard + character. No current locale uses a negativePrefix that is different than minusSign. No current locale uses either positiveSuffix or negativeSuffix.
So an easier approach would be to do:
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.maximumFractionDigits = 1;
[nf setPositivePrefix:[nf plusSign]];
[nf setNegativePrefix:[nf minusSign]];
label.text = [nf stringFromNumber:#(val)];
This case it's simple, just add the prefix:
nf.positivePrefix= nf.plusSign;
Though it won't use the user's locale, you can do the following to generate the +/- sign without the somewhat expensive overhead of an NSNumberFormatter:
// assume 'number' is an NSNumber
label.text = [NSString stringWithFormat:#"%+.02f", [number floatValue]];
Simple Case:
let f = NumberFormatter()
f.positivePrefix = f.plusSign
Currency Case :
Hack needed, because setting the prefix to plusSign only will remove the currency symbol.
let f = NumberFormatter()
f.numberStyle = .currency
f.positivePrefix = f.plusSign + f.currencySymbol
There is a bit more work depending on the locale.. The currency symbol may be before, or after, but this is probably another subject..
Edit:
Even if it is another subject, I'd say a possible solution to the problem above is to subclass NSNumberFormatter :
override func string(from number: NSNumber) -> String? {
returns ( number.doubleValue >= 0 ? super.plusSign : "" ) + super.string(from: number)
}
This way, NSNumberFormatter should manage the currency position while your subclass simply prepend the + sign. No time to test this in depth, but at least it is an approach.
The underlying formatting language for NSNumberFormatter doesn't have any provision for what you want to do -- it will allow you to specify a localized positive sign on exponents, but not for the entire formatted string. Nor does NSLocale seem to make available the localized positive sign.
Aside from making a dummy string that includes an exponent, pulling the localized positive sign out, and putting your final formatted string together by hand, I think you're out of luck.
A reusable formatter in swift:
var numberFormatter: NSNumberFormatter {
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.locale = NSLocale(localeIdentifier: "it_IT")//your Locale
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0
formatter.positivePrefix = formatter.plusSign
return formatter
}
Then use it:
let myDoubleValue = 12.00
let myStringNumber = numberFormatter.stringFromNumber(myDoubleValue)!
I don't think any of the previous answers will actually take into consideration everything you mentioned in your question.
It is true that NumberFormatter does not have an option to set the plus sign visible for all positive numbers when formatting currency values.
Also, replacing prefixes and suffixes will likely break the format for some regions and always replacing a prefix will only work if the set locale uses the currency symbol on the left.
A simple way to address this without losing the locale formatting can be seen below:
var value: Double = 3.1234
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 1
if value > 0 {
return formatter.string(for: value.negated())?.replacingOccurrences(
of: formatter.minusSign,
with: formatter.plusSign
)
} else {
return formatter.string(for: value)
}
Even though this can be seen as hack, it's an effective way to achieve everything you mentioned without manually writing a number formatter.

Adding integers which are in strings in objective-c

For example I have "$100" and "$50" in two strings, I want to add them to get an output "$150".
I know the general method(converting them into integers and adding them), but i am searching for a shorter method which does not call many functions
You can use an NSNumberFormatter to parse the string into a NSNumber, Sum them and then convert back to String :
NSString *strNum1 = "$100";
NSString *strNum2 = "$150";
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber * myFirstNumber = [f numberFromString:strNum1];
NSNumber * mySecNumber = [f numberFromString:strNum2];
NSNumber *sum = [NSNumber numberWithFloat:([myFirstNumber floatValue] + [mySecNumber floatValue])];
NSString * strSum = [f stringFromNumber:sum];
float result = [[fiftyBucks substringFromIndex:1] floatValue] + [[hundredBucks substringFromIndex:1] floatValue];
or use NSScanner, but it will be little longer, but more reliably/safely:
float fifty, hundred, result;
[[NSScaner scannerWithString: fiftyBucks] scanFloat: &fifty];
[[NSScaner scannerWithString: hundredBucks] scanFloat: &hundred];
result = fifty + hundred;
I think you better store the price at CGfloat instead.
showing a string "$100" is a front-end task and calculating the sum of the prices are back end task. These two should be seperated.
If you store the price as a CGFloat, you can simply do the maths.
And when you wanna show that string, juz implement a method.
- (NSString *)priceLabel:(CGFloat) _price {
return [NSString stringWithFormat: #"$.1f", _price];
}
Besides, don't be afraid of making a Front-end Helper model when you code. I put all this kind of minor method in this model as a class method. Wherever you need reuse this method, you can simply import the model.

Prevent small negative numbers printing as "-0"

If I do the following in Objective-C:
NSString *result = [NSString stringWithFormat:#"%1.1f", -0.01];
It will give result #"-0.0"
Does anybody know how I can force a result #"0.0" (without the "-") in this case?
EDIT:
I tried using NSNumberFormatter, but it has the same issue. The following also produces #"-0.0":
double value = -0.01;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[numberFormatter setMaximumFractionDigits:1];
[numberFormatter setMinimumFractionDigits:1];
NSString *result = [numberFormatter stringFromNumber:[NSNumber numberWithDouble:value]];
I wanted a general solution, independent of the configuration of the number formatter.
I've used a category to add the functionality to NSNumberFormater;
#interface NSNumberFormatter (PreventNegativeZero)
- (NSString *)stringFromNumberWithoutNegativeZero:(NSNumber *)number;
#end
With the implementation:
#implementation NSNumberFormatter (PreventNegativeZero)
- (NSString *)stringFromNumberWithoutNegativeZero:(NSNumber *)number
{
NSString *const string = [self stringFromNumber: number];
NSString *const negZeroString = [self stringFromNumber: [NSNumber numberWithFloat: -0.0f]];
if([string isEqualToString: negZeroString])
{
NSString *const posZeroString = [self stringFromNumber: [NSNumber numberWithFloat: 0.0]];
return posZeroString;
}
return string;
}
#end
How it works
The key feature is to ask the number formatter how it will format -0.0f (i.e., floating point minus zero) as an NSString so that we can detect this and take remedial action.
Why do this? Depending on the formatter configuration, -0.0f could be formatted as: #"-0", #"-0.0", #"-000", #"-0ºC", #"£-0.00", #"----0.0", #"(0.0)", #"😡𝟘.⓪零" really, pretty much anything. So, we ask the formatter how it would format -0.0f using the line: NSString *const negZeroString = [self stringFromNumber: [NSNumber numberWithFloat: -0.0f]];
Armed with the undesired -0.0f string, when an arbitrary input number is formatted, it can be tested to see if it is matches the undesirable -0.0f string.
The second important feature is that the number formatter is also asked to supply the replacement positive zero string. This is necessary so that as before, its formatting is respected. This is done with the line: [self stringFromNumber: [NSNumber numberWithFloat: 0.0]]
An optimisation that doesn't work
It's tempting to perform a numerical test yourself for whether the input number will be formatted as the -0.0f string, but this is extremely non trivial (ie, basically impossible in general). This is because the set of numbers that will format to the -0.0f string depend on the configuration of the formatter. If if happens to be rounding to the nearest million, then -5,000f as an input would be formatted as the -0.0f string.
An implementation error to avoid
When input that formats to the -0.0f string is detected, a positive zero equivalent output string is generated using [self stringFromNumber: [NSNumber numberWithFloat: 0.0]]. Note that, specifically:
The code formats the float literal 0.0f and returns it.
The code does not use the negation of the input.
Negating an input of -0.1f would result in formatting 0.1f. Depending on the formatter behaviour, this could be rounded up and result in #"1,000", which you don't want.
Final Note
For what it's worth, the approach / pattern / algorithm used here will translate to other languages and different string formatting APIs.
Use a NSNumberFormatter. In general, NSString formatting should not be used to present data to the user.
EDIT:
As stated in the question, this is not the correct answer. There is a number of solutions. It's easy to check for negative zero because it is defined to be equal to any zero (0.0f == -0.0f) but the actual problem is that a number of other values can be rounded to the negative zero. Instead of catching such values, I suggest postprocessing - a function that will check if the result contains only zero digits (skipping other characters). If yes, remove leading minus sign.
NSString *result = [NSString stringWithFormat:#"%1.1f", -0.01*-1];
If instead of a value you pass an instance you can check:
float myFloat = -0.01;
NSString *result = [NSString stringWithFormat:#"%1.1f", (myFloat<0? myFloat*-1:myFloat)];
Edit:
If you just want 0.0 as positive value:
NSString *result = [NSString stringWithFormat:#"%1.1f",(int)(myFloat*10)<0?myFloat:myFloat*-1];
Convert the number to NSString by taking the float or double value.
Convert the string back to NSNumber.
NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithString:#"-0.00000000008"];
NSString *st2 = [NSString stringWithFormat:#"%0.2f", [num floatValue]];
NSDecimalNumber *result = [NSDecimalNumber decimalNumberWithString:st2]; //returns 0
The NSNumberFormatter has two methods convert from Number to String, and from String to Number. What if we use method (Number) -> String? twice?
public extension NumberFormatter {
func stringWithoutNegativeZero(from number: NSNumber) -> String? {
string(from: number)
.flatMap { [weak self] string in self?.number(from: string) }
.flatMap { [weak self] number in self?.string(from: number) }
}
}

How to trim zeros after decimal point

I am trying to trim zeros after a decimal point as below but it's not giving desired result.
trig = [currentVal doubleValue];
trig = trig/100;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:0];
display.text = [formatter stringFromNumber:[NSNumber numberWithDouble:trig]];
The number is still being displayed without trimming zeros after the decimal point.
Here currentVal is the number I am entering.
For example if i pass "trig" = 123 (Initially "trig" = 123 after doing trig/100 i want to display 1.23 but it is displaying as 1.23000000).
Sometimes the straight C format specifiers do an easier job than the Cocoa formatter classes, and they can be used in the format string for the normal stringWithFormat: message to NSString.
If your requirement is to not show any trailing zeroes, then the "g" format specifier does the job:
float y = 1234.56789f;
NSString *s = [NSString stringWithFormat:#"%g", y];
Notice that there is no precision information, which means that the printf library will remove the trailing zeroes itself.
There is more information in the docs, which refer to IEEE's docs.
In case this helps someone. I wanted 1 decimal value but no '.0' on the end if the float was '1.0'. Using %g would give scientific notation for longer numbers, following ugliness worked well enough for me as high accuracy wasn't critical.
// Convert to 1 dp string,
NSString* dirtyString = [NSString stringWithFormat: #"%.1f", self.myFloat];
// Convert back to float that is now a maximum of 1 dp,
float myDirtyFloat = [dirtyString floatValue];
// Output the float subtracting the zeros the previous step attached
return [NSString stringWithFormat: #"%g", myDirtyFloat];
This will not display any decimal value after the decimal point:
display.text = [NSString stringWithFormat:#"%1.0f", trig];
This will just trim the zeros after the decimal point:
isplay.text = [NSString stringWithFormat:#"%3.2f", trig];
display.text = [display.text stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat#"0"]]];
Note, this may leave you with the trailing decimal point. "124." may happen. I guess that some smarter solution will be posted soon.
From the documentation, it looks like setFractionDigits: is only for converting the other way.
The best thing to do is probably to convert your number to an integer before formatting it e.g.
double converted = round(trig); // man round for docs
You can use also the formatting functions of stringWithFormat: of NSString, but then you will lose all the localisation advantages you get with NSNumberFormatter.
This may not be a proper solution where there is NSNumberFormetter Class, But I just did this rather then googling a lot! ;)
Here is an example, if it helps:
-(NSString*) trimZerosAfterDecimalPoint:(NSString*)string_ {
double doubleValue=[string_ doubleValue];
long leftPart=(long)doubleValue;
double rightPart=doubleValue-(double)leftPart;
NSString *rightPartAsStr=[NSString stringWithFormat:#"%f", rightPart];
int i=0;
for (i=rightPartAsStr.length-1; i>=2; i--) {
if ([rightPartAsStr characterAtIndex:i]!='0') {
rightPartAsStr=[rightPartAsStr substringWithRange:NSMakeRange(2, i-1)];
break;
}
}
if (i<2) {
string_=[NSString stringWithFormat:#"%ld", leftPart];
} else {
string_=[NSString stringWithFormat:#"%ld.%#", leftPart, rightPartAsStr];
}
return string_;
}
I just had to do this for one of my programs and heres how I went about it:
- (void) simplify{
int length = (int)[self.calcString length];
for (int i = (int)[self.calcString length]; i > 0; i--) {
if ([self.calcString rangeOfString:#"."].location != NSNotFound) {
NSRange prevChar = NSMakeRange(i-1, 1);
if ([[self.calcString substringWithRange:prevChar] isEqualToString:#"0"]||
[[self.calcString substringWithRange:prevChar] isEqualToString:#"."])
length--;
else
break;
}
self.calcString = [self.calcString substringToIndex:length];
}
}
This works
display.text = [#(trig) stringValue];
it is because of your datatype cannot be formatted is such a manner.