1

Hi all I am completing an on-line exercise where I have to create a program that reads several paragraphs of text. All numbers (written in text) has to be added together and a sum shown at the end.

I have several questions if you guys dont mind answering as I have researched this quite a bit.

This is just an example of the text that is used:

eg Just remember that you're standing on a platform at twelve o'clock and there are five trains that run every hour on nine tracks. A train can go as fast as two hundred miles an hour. One of millions of billions...

I am using the StringTokenizer so each word is read individually.

From research I have learnt to create arrays that represent each word (please see code example), this is so the words are easily represented into ints. Though i have also create variables for each of the numbers, this is prob not required though I do not understand how Java can represent a word to its significant number. (again please see code).

Though my biggest problem is how would I go about combining words like two hundred and eigthy one = 281 using a loop.

Any advice would be greatly appreciated, I know this code is far from perfect as I am continuing to learn though online material and books.

class wordsToNumberAdder

{
    public static void main()
{   

    String str = "Just remember that you're standing on a platform at twelve o'clock and there are 
    five trains that run every hour on nine tracks. A train can go as fast as two hundred miles
    an hour. One of millions of billions... ";

    StringTokenizer st = new StringTokenizer(str);


String[] digits = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine"};
String[] tens = {"twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
String[] teens = {"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"};
String[] power = {"hundred","thousand","million","billion"};

int one = 1, two = 2, three = 3, four = 4, five = 5, six = 6, seven = 7, eight = 8, nine = 9;
int twenty = 20, thirty = 30, forty = 40, fifty = 50, sixty = 60, seventy = 70, eighty = 80, ninety = 90;
int ten = 10, eleven = 11, twelve = 12, thirteen = 13, forteen = 14, fifthteen = 15, sixteen = 16;
int eighteen = 18, nineteen = 19, thirty = 30, hundred = 100, thousand = 1000; 
long billion = 1000000000;

double result = 0;
double group = 0;

while (set.hasMoreTokens()) {
        String word = set.nextToken();

for (int x = 0; x < power.length; i += 3) {

            if (word.equals(power[i])) {
            group = group * Math.pow(10, i);
            result = result + group;
            group = 0;
        }
    }
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
user2141414
  • 101
  • 1
  • 3
  • 10
  • 3
    possible duplicate of [how to convert words to number?](http://stackoverflow.com/questions/4062022/how-to-convert-words-to-number) – mwerschy Jun 29 '13 at 14:49
  • 3
    Use a consistent and logical indent for code blocks. The indentation of the code is intended to help people understand the program flow. – Andrew Thompson Jun 29 '13 at 14:53
  • And it has to be `for` loops and not `while` loops? – Hot Licks Jun 29 '13 at 17:41
  • @mwerschy: This question -- unlike the one you reference -- is a valid question which shows some effort on its own. Why would you close it with a reference to that poorly phrased question? – blubb Mar 10 '14 at 19:10

1 Answers1

2

To me there are two sub-problems here: parsing the input into a collection of number phrases ("two hundred and four". etc.) and translating those number phrases into actual values to sum.

There's no reason not to use a map for the word to value parsing. So instead of all those fields, try this out:

private static final Map<String, Long> NUMBER_MAP;
static {
  final Map<String, Long> map = new HashMap<String, Long>();
  map.put("one", 1L);
  map.put("two", 2L);
  map.put("three", 3L);
  ...
  map.put("hundred", 100L);
  map.put("hundreds", 100L);
  ...
  map.put("billion", 1000000000L);
  map.put("billions", 1000000000L);
  NUMBER_MAP = Collections.unmodifiableMap(map);
}

Couple of things about this: first, it's immutable so none of these mappings can change, and it is good practice to minimize mutability in your code. I also added plurals of several words to ease parsing. There might be a more elegant way of handling these plurals, but I kept it simple. I also have everything as longs for simplicity, too.

Now, for the parsing. Using a StringTokenizer is a good start, but you're not doing much with it. Here is how I would implement the parsing:

public static void main(final String[] args) {
  final StringTokenizer tokenizer = new StringTokenizer(paragraph.replace(".", " ").toLowerCase());
  final StringBuilder phrase = new StringBuilder();
  final Set<String> numberSet = NUMBER_MAP.keySet();

  while (tokenizer.hasMoreTokens()) {
    final String token = tokenizer.nextToken();

    if (numberSet.contains(token)) {
      if (phrase.length() > 0) {
        phrase.append(" ");
      }
      phrase.append(token);
    } else if (!IGNORED_WORDS.contains(token)) {
      processPhrase(phrase.toString());
      phrase.setLength(0);
    }
  }

  processPhrase(phrase.toString());
}

So what have I done here? First, I'm sanitizing the input string to handle periods and capitals. That way we can parse a sentence like "One Hundred." Then I use a StringBuilder to efficiently build the number phrases. If the next token (word) is in the set of keys to our numbers (e.g. "eighteen" or "hundred") I add it to the current phrase, preceded by a space if it's not the first word in the phrase. IGNORED_WORDS is an (immutable) set that just contains the String "and". This lets us parse "one hundred and ten", for example.

So what about the transformation of phrases to numbers? The loop that you wrote above doesn't make much sense to me. What is i? What is x? What this a typo? The general approach that I used was to consider two words at a time. If there's only one, then that's easy and we are done. If there's two, though, we have to consider their order. Take "nine hundred" for example. Because the second value (100) is greater than the first (9), we'll multiply them together and add them to the total sum. If the previous calculated value is greater than the current one, we just add them. That way, parsing "twelve thousand two hundred" looks like:

12 < 1000 : current sum = 12 * 1000 = 12000
2 < 100 : current sum += 2 * 100 = 12000 + 200 = 12200

I did not test it too much, but you can see my implementation of it here.

Addendum

Maps are really cool in any programming language. Maps are a one-to-one (bijection) function: the keys in a Java map are the domain of the function, and the values the keys are mapped to are the range. You can extract the set of all keys with map.keySet(), and the values with map.values() (where map is an instantiated Map variable). An even more powerful feature is the following:

for (Map.Entry<K, V> entry : map.entrySet()) { 
  // loop over all entries in the map 
}

This is really an iterator over each mapping in the map, and you can get the key and the value on each iteration.

Anyways, if we have a map variable, and go map.keySet(), we get a Java Set of all the keys in that map. In the instance of this post, this Set would include the strings "one", "two", ... "billions". To put it in other words, if I had a collection of statements "A maps to B", and asked for the keySet, I would get all the 'A's in those statements.

So view NUMBER_MAP as a mapping from English numbers (keys) to mathematical numbers (values). Starting from the English number in the paragraph, I wanted to get the mathematical number. To do this, I compared the value from the paragraph to the keys from the map.

lealand
  • 367
  • 4
  • 13
  • thank you so much for your time and effort in explaining this to me – user2141414 Jun 29 '13 at 20:13
  • - mapping was a new area for myself and will be a valuable resource later in my career, as i have just started as a grad developer and trying to learn as much as possible. Again thank you for your time and efforts. – user2141414 Jul 01 '13 at 15:29
  • Hello I have pulled apart your example the only one bit that i am unsure of now is: final Set numberSet = NUMBER_MAP.keySet(); I have tried researching what .keySet does, but could you explain it to me in layman terms please? – user2141414 Jul 01 '13 at 19:24
  • Sorry for late reply. I have expanded my answer. – lealand Jul 06 '13 at 05:16