Find index of double number delimiter

Posted on

Problem

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?

Solution

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.

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 π.

Leave a Reply

Your email address will not be published. Required fields are marked *