1

I have one Fields planValue_c set as %age in salesforce. Possible inputs when reading from file are like

20 -Integer
20 2/3- Integer with fraction ( space is delimiter)
2/3 - Fraction

Assuming integer with fraction will always have space a determiner.

Decimal.value(String) is throwing error on in case fraction. as when something like 20 2/3 is assigined to %age its throwing error.

I was trying for some logic like

if(String.isNotBlank(input)){
    if (String(input).contains(space))// It means it has fraction, What is expression for space would it be /t or '' 
    {
        Decimal value=String.SubstringAfter(space);// to separte fraction after space
        //Math.round(value)// How can i get fraction value here calculated to decimal

    }

    else if(String.size()=1)// Means it can only either fraction or Interger
        try {
            Decimal newInput=decimal.value(input);//if it is integer it should pass
        }
    catch (TypeException t){

        //if it was fraction sinlge like 2/3 it should come here

    }

}
  • Why not just insist on the standard decimal format? This functionality seems of dubious value. – Adrian Larson Feb 9 '17 at 1:27
  • Value is comming from some third party. We cannot control them. there users are sending all three formats and we have % age type field at salesforce as buisness want it to use into reports – David Mycka Feb 9 '17 at 1:29
  • Can you just trim the fraction entirely? Seems like it could be good enough accuracy. – Adrian Larson Feb 9 '17 at 1:32
  • Actually as this involves ratings system, this will impact premium they want to show on reports. When many records got trimmed this way – David Mycka Feb 9 '17 at 1:33
3

You might consider regular expressions here:

public static Decimal parse(String input)
{
    try
    {
        return Decimal.valueOf(input);
    }
    catch (TypeException t)
    {
        Matcher m = Pattern.compile('(\\d+)\\s+(\\d+)\\s*\\/\\s*(\\d+)').matcher(input);            if (m.find()) return Decimal.valueOf(m.group(1)) +
            Decimal.valueOf(m.group(2)) / Decimal.ValueOf(m.group(3));
    }
    return null;
}

The expression itself is pretty straightforward as these things go. You can always feed a regular expression into Regex 101 and get great explanations:

(\d+)\s+(\d+)\s*\/\s*(\d+)
  • 1st Capturing Group (\d+)

    • \d+ matches a digit (equal to [0-9])
    • + Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
  • \s+ matches any whitespace character (equal to [\r\n\t\f\v ])

    • + Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
  • 2nd Capturing Group (\d+)

    • \d+ matches a digit (equal to [0-9])
    • + Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)
  • \s* matches any whitespace character (equal to [\r\n\t\f\v ])

    • * Quantifier — Matches between zero and unlimited times, as many times as possible, giving back as needed (greedy)
  • \/ matches the character / literally (case sensitive)

  • \s* matches any whitespace character (equal to [\r\n\t\f\v ])

    • * Quantifier — Matches between zero and unlimited times, as many times as possible, giving back as needed (greedy)
  • 3rd Capturing Group (\d+)

    • \d+ matches a digit (equal to [0-9])
    • + Quantifier — Matches between one and unlimited times, as many times as possible, giving back as needed (greedy)

You feed the capturing groups back into Decimal.valueOf using m.group(groupNumber).

You can test this parser pretty easily as well:

system.assertEquals(1, parse('1'));
system.assertEquals(3.14, parse('3.14'));
system.assertEquals(66.67, parse('66 2/3').setScale(2));
system.assertEquals(1.5, parse('1   1 / 2'));

If you want to make the leading number optional, it gets a bit more complicated. I'll try to add an explanation tomorrow, but basically you need to:

  • make the capturing group optional (? just after the parens)
  • make the whitespace quantifier between the first and second capturing groups optional (* instead of +)
  • include a whitespace character inside the first capturing group to compensate

Now that one of them might be null, it will be easier to handle these strings if you cache the groups.

// inside catch block
Matcher m = Pattern.compile('(\\d+\\s)?\\s*(\\d+)\\s*\\/\\s*(\\d+)').matcher(input);
if (!m.find()) return null;

String d1 = m.group(1), d2 = m.group(2), d3 = m.group(3);
Decimal base = String.isBlank(d1) ? 0 : Decimal.valueOf(d1.trim());
Decimal fraction = Decimal.valueOf(d2) / Decimal.valueOf(d3);
return base + fraction;
  • Thanks Adrian, you are aweseome, i am getting following results.. system.debug(''+parse('66 2/3')); =66.666666666666666666666666666666667 system.debug(''+parse('2/3')); =null system.debug(''+parse('66.89'));= 66.89 – David Mycka Feb 9 '17 at 3:54
  • Please help me here, i have never used RegEX, could you please guide me where to put ?..I am really thankful to you – David Mycka Feb 9 '17 at 4:01
  • Thanks Adrian, it was really helpful..let me try some testing around it – David Mycka Feb 9 '17 at 4:23
  • It was so cool, i do not have words to express. I wish i could be a good programmer like you in some time in my life.. Thanks a lot again. :) – David Mycka Feb 9 '17 at 4:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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