4

I want to convert these types of values, '3 million', '24 thousand', '350 thousand', etc. to a number. In either JavaScript(so i can do it client side) or PHP(server side). is there a function for this? like PHPs strtotime() function?

input                  output
3 million              3000000
24 thousand            24000
350 thousand           350000
Raymond Ativie
  • 1,747
  • 2
  • 26
  • 50
  • 1
    I'm not aware of such a function. You could hack your way thru it though. Doing something like `['million' => '000000'...etc']` but this is really prone to errors. – Andrei Jul 23 '15 at 12:11
  • 1
    I'm not entirely sure this is a duplicate, but it seems relevant: http://stackoverflow.com/questions/11980087/javascript-words-to-numbers – David Thomas Jul 23 '15 at 12:17
  • I think the only way is mentioned in comment one – Sunil Pachlangia Jul 23 '15 at 12:22

6 Answers6

3

do you want to combine them as well?

function convert(num) {
  function mapGroups(str) {
    if (/million/.test(str)) {
      return str.match(/\d+/)[0] * 1000000;
    }
    if (/thousand/.test(str)) {
      return str.match(/\d+/)[0] * 1000;
    }
    if (/hundred/.test(str)) {
      return str.match(/\d+/)[0] * 100;
    }
    return +str;
  }
  
  return num.match(/(\d+)( ?\w* ?)/g)
            .map(mapGroups)
            .reduce(function(p, c) { return p + c });
}

console.log(convert("3 million 240 thousand 7 hundred 54")) //3240754
console.log(convert("5 millions 7 hundreds 54")) //5000754

it is very basic but you do get a grip.

adiga
  • 34,372
  • 9
  • 61
  • 83
webduvet
  • 4,220
  • 2
  • 28
  • 39
1

There is no miracle solution but the NumberFormatter::parse PHP function may help you solve your issue :

$fmt = numfmt_create('en_US', NumberFormatter::SPELLOUT);
echo numfmt_format($fmt, '1234567');

Will output one million two hundred thirty-four thousand five hundred sixty-seven and

$fmt = numfmt_create('en_US', NumberFormatter::SPELLOUT);
echo numfmt_parse($fmt, 'one million two hundred thirty-four thousand five hundred sixty-seven');

Will output 1234567.

Sadly, this will not work with mixed-type value like "24 thousand" (it needs to be entirely spelled out) but you may throw together a workaround by spelling out actual digits in your string first.

Dorian
  • 211
  • 2
  • 5
0
    $str = str_replace('hundred','00',$str);
    $str = str_replace('thousand','000',$str);
    $str = str_replace('million','000000',$str);
    $str = str_replace('billion','000000000',$str);
$str = str_replace(' ','',$str);

Like that :)

Hearner
  • 2,711
  • 3
  • 17
  • 34
0

Assuming:

  1. you have the numeric part ALWAYS written in number format
  2. the unit are consistent and spelled correctly, that is you always have 'million' and not 'millions', or 'milion'
  3. the number syntax is correct (something like 3 million, 24 thousand, ecc)

you could use a map like:

var unitMap = [million:6, thousand:3, '':1] // add as many units as you like

and then convert the string into a number. Here's an idea using javascript and jQuery.

var UNIT_SEPARATOR = ', '; // what separates the number parts (millions from thousands, ecc)
var VALUE_UNIT_SEPARATOR = ' '; // what goes between the value and the unit
var unitMap = {
    billion:9,
    million:6,
    thousand:3,
    '':1
}; // add as many units as you like

$('#convert').click(function () {
    var number = $('#input').val();
    var numberParts = number.split(UNIT_SEPARATOR);
    var convertedNumber = 0;

    for (var i = 0; i < numberParts.length; i++) {
        var splitUnit = numberParts[i].split(' ');
        var value = parseFloat(splitUnit[0]);
        var exp = (splitUnit.length === 1) ? 0 : parseInt(unitMap[splitUnit[1]]);

        var temp = value * Math.pow(10, exp);

        convertedNumber += temp;
    }

    $('#result').text(convertedNumber);
});

Here's the fiddle

NOTE this is a hint. Input should be sanitized and validated before using this. Also this approach is very error-prone, so you should test it carefully before applying it (se the above assumptions).

Hope it helps! ;)

ANVerona
  • 439
  • 3
  • 10
0

I enjoy problems like these, here is my take on it in PHP. I wanted to make it as compact as possible, and it is easy to extend if you want to use thousandths or millionths.

I had to add the + 0 to deal with the possibility of a trailing +, which works but I'm not a fan of how it looks. If there comes a need to filter for characters prior to eval(), I'd introduce some regex and just get rid of \+$.

$find_replace = array(
    array('hundred', 'thousand', 'million', 'billion'),
    array('* 1e2 +', '* 1e3 +', '* 1e6 +', '* 1e9 +')
);

function words_to_number($input) {
    global $find_replace;
    $equation = str_replace($find_replace[0], $find_replace[1], $input) . ' + 0';
    $eval_result = eval('$result = ' . $equation . ';');
    return $eval_result === false ? $eval_result : $result;
}

$words = '3 million 240 thousand 7 hundred 54';
$number = words_to_number($words);
echo $words . ($number === false ? ' error!' : " == $number") . "\n";
// 3 million 240 thousand 7 hundred 54 == 3240754

$words = '5 thousand';
$number = words_to_number($words);
echo $words . ($number === false ? ' error!' : " == $number") . "\n";
// 5 thousand == 5000
mike.k
  • 3,277
  • 1
  • 12
  • 18
-1

This example was written to convert numbers to words, but you can convert words to numbers in many cases- as long as the word input matches what would be output from the number to words calculation.

You are welcome to take what you can use and discard the rest.

<!doctype html>
<html lang= "en">
<head>
<meta charset= "utf-8">
<title>Number Strings</title>
<style>
textarea{
    font-size: 1em;
    color: black;
    font-weight: 600;
    resize: both;
}
button{
    cursor: pointer;
    text-decoration: none;
    color: black;
    font-size: 1em;
    font-weight: bold;
}
button: hover, button: focus{
    color: red;
}
p{
    font-size: 1.1em;
    font-weight: 500;
    position: relative;
    margin: 1em 1ex;
}
.leftcolumnDiv{
    display: inline-block;
    position: relative;
    max-width: 60em;
    min-width: 24em;
    border: thick navy ridge;
    border-radius: 1em;
    padding: 1em;
}
#numword_div{
    color: black;
    font-weight: 600;
    font-size: 1em;
    padding: 1em;
    font-family: sans-serif;
}
#numword_div span{
    color: green;
}
</style>

<script>
// utility for display demo and object container for number/word methods

function mr(hoo){
    if(typeof hoo== 'string') hoo= document.getElementById(hoo) || '';
    return (hoo.nodeType== 1)? hoo: null;
}

var Yankee= {
    dr: function(what, pa, hoo, txt, sty){
        var tag, el, mod, ref;
        what= what.toLowerCase().split('_');
        tag= what[0], mod= what[1], ref= mr(pa);
        el= document.createElement(tag);
        if(hoo) el.id= hoo;
        if(sty) el.style.cssText= sty;
        if(ref){
            if(mod== 'b4') ref.parentNode.insertBefore(el, ref);
            else if(mod== 'bf' && ref.firstChild){
                ref.insertBefore(el, ref.firstChild);
            }
            else ref.appendChild(el);
        }
        if(txt) el.appendChild(document.createTextNode(txt));
        return el;
    },
    zap: function(who){
        who= mr(who);
        while(who.lastChild) who.removeChild(who.lastChild);
    },
    zappit:function(who){
        who= mr(who);
        who.parentNode.removeChild(who);
    }
}

/* US English words from numbers and back*/
Math.gcd= function gcd(a, b){
    if(b) return gcd(b, a%b);
    return Math.abs(a);
}
Number.fromRoman= function(s){
    s= String(s).toUpperCase();
    if(s.length>15 ||  /[^MDCLXVI]/.test(s)) return NaN;
    var L= s.length, sum= 0, i= 0, next, val, 
    R={
        M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1
    };
    while(i<L){
        val= s.charAt(i++);
        if(!R[val]) return NaN;
        val= R[val];
        next= R[(s.charAt(i) || 'N')] || 0;
        if(next>val) val*= -1;
        sum+= val;
    }
    if(sum.toRoman()== s) return sum;
    return NaN;
}
Number.prototype.toRoman= function(){
    var n= Math.floor(this), val, s= '', limit= 3999, i= 0, 
    v= [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], 
    r= ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
    if(n<1 || n>limit) return '';
    while(i<13){
        val= v[i];
        while(n>= val){
            n-= val;
            s+= r[i];
        }
        if(n== 0) return s;
        ++i;
    }
    return '';
}

Yankee.NW={
    nameNumbers: function(str){
        var Y= Yankee.NW, nw= Y.numberWords, 
        s= (str+'').replace(/([^\d])(?=\.\d+)/g, '$1 0').trim();
        var f, w= '', x, xs, n, ns, M, 
        rx=/(-?\d+)(\.(\d+)([eE]([+-]?\d+))?)?/g, 
        rx2=/(\d+)(\.(\d+)([eE]([+-]?\d+))?)?/, 
        rq=/((\d+)? +(and +)?)?(\d+)\/(\d+)/g;
        s= s.replace(rq, function(a){
            return Y.printFraction(a);
        });
        s= s.replace(rx, function(q){
            n= +q, 
            ns= n+'';
            M= rx2.exec(ns) || [];
            w= (n<0)? 'minus ': '';
            if(M[1]) w+= nw(M[1]);
            if(M[3]){
                f= M[3].split('').map(function(itm){
                    return nw(itm);
                });
                w+= ' point '+ f.join(' ');
                if(M[5]){
                    x= +M[5];
                    xs= x<0? 'minus ': '';
                    xs+= Y.ordinal(nw(x));
                    w+= ' times ten to the '+xs;
                }
            }
            return w || q;
        });
        return s.replace(/ {2,}/g, ' ');
    },
    numberWords: function numberWords(x){
        var n= +x;
        if(isNaN(n) || n%1) return ''+x;
        n= Math.abs(n);
        var i= 0, p, prefix= [], num, rem, 
        NK= Yankee.NW.numberwords_array, 
        w= NK[0], w1= NK[1], w2= NK[2], mag= NK[3];
        while(n>99){
            x= mag[i];
            if(n>= x){
                p= Math.floor(n/x);
                n%= x;
                prefix.push(numberWords(p)+w2[i]);
            }
            ++i;
        }
        if(prefix.length){
            prefix= prefix.join(', ');
            if(n) prefix+= ' and ';
            else return prefix;
        }
        else prefix= '';
        if(n<20) num= w[n];
        else{
            num= w1[Math.floor(n/10)];
            rem= n%10;
            if(rem) num+= ' '+w[rem];
        }
        return prefix+num;
    },
    numberwords_array: [
        ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 
        'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 
        'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'], 
        ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 
        'seventy', 'eighty', 'ninety'], 
        [' quadrillion', ' trillion', ' billion', ' million', 
        ' thousand', ' hundred'], 
        [1e15, 1e12, 1e9, 1e6, 1e3, 100]
    ], 
    ordinal: function(s){
        var ax, rx, suffix, str= ' '+s.trim();
        ax= str.lastIndexOf(' ');
        suffix= str.substring(ax).toLowerCase();
        if(str.charAt(str.length-1)== 'y'){
            return str.slice(0, -2)+'tieth';
        }
        if(ax>1) str= str.substring(0, ax);
        else str= ' ';
        switch(suffix.trim()){
            case 'one': return str+' first';
            case 'two': return str+' second';
            case 'three': return str+' third';
            case 'five': return str+' fifth';
            case 'eight': return str+' eighth';
            case 'nine': return str+' ninth';
            case 'twelve': return str+' twelfth';
            default: return str+suffix+'th';
        }
    },
    printFraction: function(s){
        var str= s.replace('and ', '').trim()
        var num, Y= Yankee.NW, RN= Y.numberWords, 
        M=/((\d+)? +)?(\d+)\/(\d+)/.exec(str);
        if(!M) return str;
        num= M[2]? RN(M[2])+' and ': '';
        num+= RN(+M[3])+'-';
        if(M[4]=== '2') num+= 'half';
        else if(M[4]=== '4') num+= 'quarter';
        else num+= Y.ordinal(RN(+M[4]));
        if(M[3]!== '1') num+= 's';
        return num;
    },
    /* words to numbers */
    fromFraction: function(str, prec){
        var rx=/(\d+)\/(\d+)/, dec= 0, I= 0, 
        M= rx.exec(str);
        if(M){
            if(M.index>0) I= parseFloat(str);
            if(M[1]) dec= M[1]/M[2];
            if(!prec) prec= str.length+1;
            if(typeof prec== 'number') dec= +(dec.toFixed(prec));
            return I+dec;
        }
        return parseFloat(str);
    },
    reDigit: function(n){
        var sub= [], tem, d= n.match(/[a-z]+/g), L= d.length-1, 
        nwords= Yankee.NW.wordnumber_keys;
        d.map(function(w, i){
            tem= nwords[w];
            if(isFinite(tem)){
                if(tem<20 || i== L) sub.push(tem+'');
                else sub.push((tem+'').charAt(0));
            }
        });
        return sub.join('');
    },
    reFraction: function(str){
        var M, w= 0, n, d, f2= 0, prec, ax;
        ax= str.lastIndexOf(' and ');
        if(ax== -1){
            ax= 0;
            w= 'zero';
        }
        else{
            w= str.substring(0, ax);
            ax+= 4;
        }
        M= str.substring(ax).split('-');
        n= M[0];
        d= M[1];
        if(n && d){
            d= Yankee.NW.wordsToNumber(d);
            n= Yankee.NW.wordsToNumber(n);
            return [w, n/d];
        }
        return [w];
    },
    wordNumber: function(str){
        var Y= Yankee.NW, n= 0, sign= 1, whole= 0, dec= 0, frac, 
        suffix= 0, exp= 1, exsign= 1, prec, pt, 
        rx1=  /(illion|thousand|hundred)/i, 
        rx2=/( +times ten to the( +minus)? +)/g, 
        rx3=/(point|times|[^a-z, ])/, 
        s= str.toLowerCase().trim().replace(/ieth?$/, 'y').replace(/(ths?|s)$/, '');
        if(s.indexOf('minus ')== 0){
            sign= -1;
            s= s.substring(5);
        }
        if(!rx3.test(s)){
            n= (!rx1.test(s))? Y.reDigit(s): Y.wordsToNumber(s);
            return n*sign;
        }
        whole= s;
        var pt= s.split('point ');
        dec= pt[1];
        if(dec){
            whole= pt[0];
            temp= rx2.exec(dec);
            if(temp!= null){
                if(temp[2]) exsign= -1;
                suffix= dec.substring(rx2.lastIndex);
                dec= dec.substring(0, temp.index);
                suffix= Y.wordsToNumber(suffix);
                exp= Math.pow(10, suffix*exsign);
            }
            dec= Y.reDigit(dec);
            prec= dec.length+1;
            dec= +('0.'+dec);
        }
        else if(whole.indexOf('-')!= -1){
            pt= Y.reFraction(whole);
            whole= pt[0];
            dec= pt[1];
        }
        n= Y.wordsToNumber(whole.replace(/ +and +/g, ' '));
        if(dec){
            n+= dec;
            if(!prec) frac= Math.min(String(n).length, 15);
            n= n.toPrecision(frac);
        }
        if(exp!== 1) return sign*((n*exp).toExponential(prec));
        return sign*n;
    },
    wordnumber_keys:{
        billion: 1e9, eigh: 8, eight: 8, eighteen: 18, eightt: 8, eighty: 80, 
        eleven: 11, fif: 5, fifteen: 15, fifty: 50, first: 1, five: 5, forty: 40, 
        four: 4, fourteen: 14, half: 2, hundred: 100, million: 1e6, nin: 9, 
        nine: 9, nineteen: 19, ninety: 90, one: 1, quadrillion: 1e15, quarter: 4, 
        second: 2, seven: 7, seventeen: 17, seventy: 70, six: 6, sixteen: 16, 
        sixty: 60, ten: 10, third: 3, thirteen: 13, thirty: 30, thousand: 1e3, 
        three: 3, trillion: 1e12, twelf: 12, twelve: 12, twenty: 20, two: 2, zero: 0
    },
    wordsToNumber: function(s){
        var w= s.toLowerCase().replace(/(thousand|[mbr]illion),?/g, '$1,');
        var n= 0, nArray= w.split(/,/), 
        sub= 0, tem, segment, 
        nwords= Yankee.NW.wordnumber_keys;
        while(nArray.length){
            sub= 0;
            segment= nArray.shift().match(/[a-z]+/g) || [];
            segment.forEach(function(w2){
                tem= nwords[w2.trim()];
                if(isFinite(tem)){
                    if(tem<100) sub+= tem;
                    else sub*= tem;
                }
            });
            n+= sub;
        }
        return n;
    },
    //demo
    display: function(skip){
        var who= mr('strum_in'), nw= Yankee.NW, 
        v= who.value.trim(), v2, n2, p, w= '';
        if(!/[a-z\d]+/i.test(v)) return;
        var didit= mr('numword_div').textContent || mr('numword_div').innerText||'';
        if(didit.indexOf(who.value+'=')== 0) return;
        if(/^[IVXLCDM]+$/.test(v)) v= Number.fromRoman(v);
        if(/^[a-z ,-]+$/i.test(v)){
            v2= nw.wordNumber(v);
        }
        else v2= nw.nameNumbers(v);
        p= Yankee.dr('p_bf', 'numword_div', '', who.value+'= ');
        Yankee.dr('span', p, '', v2, 'color:green');
        mr('numword_div').scrollTop= 0;
        mr('strum_in').focus();
    },
    sampler: function(){
        var nw= Yankee.NW, v2, v;
        Yankee.zap('numword_div');
        mr('strum_in').value= '';
        var A= ['150', '0.12', 'fourteen hundred and ninety two', '65 1/16', '65.0045', 
        'thirty three and one- third', '2012', '98.6', 'MCMLIX', '897456971.25', 
        '1 1/2', '12 3/4', '-9', '33 1/3', Math.PI, 'nine and three- quarters', 
        'seventy eight point six four','XIX',  '1.1550046210e+17', '1.222e3', 'minus two hundred', 
        '4.5e-10', '3.125e-3', '1.56760e+25'].forEach(function(sv){
            mr('strum_in').value= sv;
            nw.display();
        });
        mr('numword_div').scrollTop= 0;
        mr('strum_in').focus()
    }
}
window.onload= function(){
    mr('toWordsBtn').onclick= Yankee.NW.display;
    mr('demoWordsBtn').onclick= Yankee.NW.sampler;
    mr('clearWordsBtn').onclick= function(){
        Yankee.zap('numword_div');
        mr('strum_in').focus();
    }
}
</script>
</head>
<body>
<p class= "breadcrumbs"><a href= "../webshop.html">Home</a> </p>
<h1>Numbers to Words, Words to Numbers<br>
<span style= "font-weight: bold; color: navy; font-size:smaller">US- English version</span> </h1>
<div class= "leftcolumnDiv">
<p>Besides digits, numerical input can include a negative sign(-50), 
decimal point(.), exponential(1.15e+25) or a real number with a division slash(1/2 or 9 3/4).</p>
<p>You can also find a number value from word input, if the words are formed
as the parser expects.(Any output word strings can be used as input.)</p>
<h3 style= "margin-top: 1em;">Enter a value to convert to(or from) words</h3>
<p id= " ">Use the "Demo" button for some examples.</p>
<p>
<textarea rows= "2" cols= "40" id= "strum_in"></textarea><br>
<button id= "toWordsBtn" type= "button" style= "margin-left: 1em;">Convert</button>
<button type= "button" id= "demoWordsBtn">Demo</button>
<button id= "clearWordsBtn" type= "button">Clear</button>
</p>
<div id= "numword_div"></div>
</div>
</body>
</html>
kennebec
  • 102,654
  • 32
  • 106
  • 127