8
\$\begingroup\$

I have a String with a double number. Unfortunately, the number is created on backends with different locals, so it could be both 101.02 and 101,02 (different delimiters). I need to get the position of this delimiter if it exists and get 0, if it is not.

I've come to two options:

int pos = amount.indexOf(',') == -1 ?
                (amount.indexOf('.') == -1 ? 0 : amount.indexOf('.'))
                : amount.indexOf(',');

Second option with the same logic but different style:

int pos = amount.indexOf(',');
if (pos == -1) pos = amount.indexOf('.');
if (pos == -1) pos = 0;

I do not need to have a double number from String, I just need the position of the delimiter to color the String (using the Android class Spannable).

Is there a cleaner way to achieve this goal? And which of these styles are better, in your opinion? Is there some way to use the DecimalFormat class to achieve the goal?

\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

The problems with the first approach

int pos = amount.indexOf(',') == -1 ?
                (amount.indexOf('.') == -1 ? 0 : amount.indexOf('.'))
                : amount.indexOf(',');

are that:

  • It's not that easily readable; the ternary operator has value when what is tested is simple enough. But when you start nesting them, it often degrades clarity.
  • The index is calculated two times, one to test whether it is -1 or not, and the second time to return the value.

As such, the second approach

int pos = amount.indexOf(',');
if (pos == -1) pos = amount.indexOf('.');
if (pos == -1) pos = 0;

is the most preferable between the two, mainly for clarity. You should consider putting that into a utility method.

There would be other approaches like using a regular expression, but another good one would to not traverse the string potentially 2 times, and instead of looking whether the string has a certain character, loop through each character and see if it is one of the potential delimiters. With Java 8, you could have

int pos = amount.chars().filter(c -> c == '.' || c == ',').findFirst().orElse(0);

And you could write explicitly the for loop for Java ≤ 7.

Final point: having a position of 0 when neither , nor . are present in the String can be confusing; 0 is a valid index value for a string, and it can imply that the delimiter was the first character of the string. If you consider ".25" (that could be a valid representation of a double number, the 0 before being implied), the code would consider this as if having no delimiter.

\$\endgroup\$
3
  • \$\begingroup\$ By the way, is it really worth to "write my own bicycle" to use only one "for" cycle (in Android I can't use Java 8 to support old devices) instead of making two calls to indexOf()? It is front-end and there are at most 50 elements on one screen where it is needed to make such check. \$\endgroup\$
    – Gaket
    Dec 1, 2016 at 11:10
  • 2
    \$\begingroup\$ @Gaket the cost of indexOf on a small set of small Strings is very low compared to operations that manipulate pixels on the screen, so I wouldn't bother. Just leave a comment that it might be optimized later if needed :) \$\endgroup\$ Dec 1, 2016 at 11:37
  • 1
    \$\begingroup\$ The second option is better. I wouldn't optimize anything except, maybe, if you knew that one source locale dominated the input. Then I'd check for that locales delimiter first. \$\endgroup\$
    – Kristian H
    Dec 1, 2016 at 12:23
2
\$\begingroup\$

I prefer to make it obvious that pos is getting assigned a value, one way or another. To that end, a ternary expression would be good.

But the way you wrote your ternary condition is confusing. Inverting the condition would make it more readable.

int pos = amount.indexOf(',') >= 0 ? amount.indexOf(',') :
          amount.indexOf('.') >= 0 ? amount.indexOf('.') : 0;

I wouldn't worry much about calling .indexOf() excessively. These strings are short, and performance is not likely to be an issue.

By biggest concern, though, is why the fallback value is 0. A result like that could indicate either an initial ., an initial ,, or no decimal separator at all!

A word of caution: in some locales, the , or . could also be the thousands grouping separator, so there might not be a definitive way to know whether "3,141" should be interpreted as three thousand one hundred forty-one or as an approximation to π.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.