tags:

views:

276

answers:

4

I would like to ask for help with this, I wanted to check for a string matching a decimal followed by a letter (15M, 15.3M, 15T or 15.3T) and replace it with zeroes.

Like these: 15M -> 15000000 15.3M -> 15300000 15T -> 15000 15.3T -> 15300

I tried doing this with str_replace but can't get it right. Hope someone can help me.

A: 

In Perl:

#!/usr/bin/perl

use strict;
use warnings;

my %factors = (
    B => 1_000_000_000,
    M => 1_000_000,
    T => 1_000,
);

while ( <DATA> ) {
    next unless m{
        ^
        (?<number> -? [0-9]+ (?:\.[0-9]+)? )
        (?<factor>B|M|T)?
        $
    }x;
    my $number = $+{factor}
               ? sprintf( '%.0f', $+{number} * $factors{ $+{factor} } )
               : $+{number}
               ;
    print $number, "\n";
}

__DATA__
15M
15B
15T
15.3M
15.3B
15.3T
15
15.3

Output:

C:\Temp> z
15000000
15000000000
15000
15300000
15300000000
15300
15
15.3
Sinan Ünür
can you translate it in php? i don't know perl, thanks for answering
espontaneo
Sorry, I did not realize this was a PHP question. Codebender's solution is somewhat equivalent to mine.
Sinan Ünür
yes, but still, thanks for answering. i appreciated it
espontaneo
Too bad PHP doesn't support named captures, that's pretty cool.
Tim Sylvester
They are a recent addition to Perl, too.
Sinan Ünür
+3  A: 

"T" could be "thousand" or "trillion", you know.

$s = "15.7T";

$factors = Array('T' => 1000, 'M' => 1000000, 'B' => 1000000000.);

$matches = Array();
if (0 < preg_match('/([0-9]+(?:\.[0-9]*)?)([TMB])/', $s, $matches)) {
    print("$s -> " . $matches[1] * $factors[$matches[2]]);
}

prints:

15.7T -> 15700

edit:

Anchoring (makes any garbage on the front or back mean no match):

'/^(...)$/'

You may want to make whitespace allowed, however:

'/^\s*(...)\s*$/'

You can also use "\d" in place of "[0-9]:

'/(\d+(?:\.\d*)?)([TMB])/'

Case insensitivity:

'/.../i'
...
print("$s -> " . $matches[1] * $factors[strtoupper($matches[2])]);

Optional factor:

'/([0-9]+(?:\.[0-9]*)?)([TMB])?/'
$value = $matches[1];
$factor = isset($matches[2]) ? $factors[$matches[2]] : 1;
$output = $value * $factor;

Use printf to control output formatting:

print($value) -> 1.57E+10
printf("%f", $value);  -> 15700000000.000000
printf("%.0f", $value);  -> 15700000000

Stephan202 recommended a clever trick, put the "?" (optional) within the parens and you're guaranteed a match string, it just might be blank. Then you can use the blank as an array key to get the default value without having to test whether it matched or not.

Putting all that together:

$factors = Array('' => 1, 'T' => 1e3, 'M' => 1e6, 'B' => 1e9);
if (0 < preg_match('/^\s*(\d+(?:\.\d*)?)([TMB]?)\s*$/i', $s, $matches)) {
    $value = $matches[1];
    $factor = $factors[$matches[2]];
    printf("'%s' -> %.0f", $s, $value * $factor);
}
Tim Sylvester
+1. However, you might want to anchor the pattern as well as making the factor optional just in case there are numbers without data.
Sinan Ünür
Yes, I was trying to keep it simple, since there were no clues about what context the input might be in.
Tim Sylvester
this is what i need, thankshow will i anchor the pattern?
espontaneo
hey i just checked, if its in millions, it returns the number in exponential form(1.4M -> 1.4E+6), how will i convert that with zeroes?
espontaneo
Hmm, I can get "15.7B -> 15700000000", but I have "precision = 14" set in my PHP.ini file. I'll update the answer.
Tim Sylvester
That's why I was using `sprintf` in my answer. See http://php.net/sprintf
Sinan Ünür
oh, i see, thanks for this, i learned a lot
espontaneo
Stephan202, which part of the code will the ? be? The default value is appealing
espontaneo
I was referring to 1. the change from "([TMB])?" to "([TMB]?)" 2. the change from "isset($matches[2]) ? $factors[$matches[2]] : 1;" to simply "$factors[$matches[2]]" and 3. the addition of '' => 1 to the factor array.
Tim Sylvester
+2  A: 

Alternative version which performs a conversion on all prices found:

  $multiply = array('' => 1, 'T' => 1000, 'M' => 1000000);

  function convert($matches) {
    global $multiply;
    return $matches[1] * $multiply[$matches[3]];
  }

  $text = '15M 15.3M 15.3T 15';
  print(preg_replace_callback('/(\d+(\.\d+)?)(\w?)/', 'convert', $text));
Stephan202
Nice, I didn't even know that function existed. It would be even cooler if you could make the factor mapping a parameter and pass in a curried version for the callback, but currying in PHP is a pain IIRC. Clever use of the blank string as a key, too.
Tim Sylvester
A: 

Have no idea how to make it in one regular expression, here is my try in Ruby.

#!/usr/bin/ruby

def trans s
h={"M"=>10**6 ,"T"=>10**3}
a = s.split(/[MT]/) + [s.split("").last]
(a[0].to_f * h[a[1]]).to_i
end


puts trans "15M"
puts trans "15.3M"
puts trans "15T"
puts trans "15.3T"
pierr
OP has already stated its PHP.
ghostdog74