Repository: MycroftAI/mycroft-core

Issue:
extract_number('the third one') returns 1
When a user is asked to select from a list of options eg "Would you like apples, bananas or oranges?", `extract_number` can be used to parse an ordinal response eg 'second' or 'third'. However if the user says 'the third one' this returns `1` instead of `3`.

## To Reproduce
`extract_number('the third one', ordinals=True)`

## Cause
parse_en.py correctly picks up `third` however on [line 268](https://github.com/MycroftAI/mycroft-core/blob/d1298744f1310ad7dcfe3b5c7326826d3d38a164/mycroft/util/lang/parse_en.py#L268) the current `val` is written to `prev_val` such that `one` overwrites `third`.

## Possible solution
Replacing [line 268](https://github.com/MycroftAI/mycroft-core/blob/d1298744f1310ad7dcfe3b5c7326826d3d38a164/mycroft/util/lang/parse_en.py#L268) with the following works:
```
if prev_val and val is 1 and ordinals:
    val = prev_val
else:
    prev_val = val
```

This passes the existing `test_parse` unit tests, and some additional assertions:
```
self.assertEqual(extract_number("the second one", ordinals=True), 2.0)
self.assertEqual(extract_number("the fourth one", ordinals=True), 4.0)
self.assertEqual(extract_number("the thirty sixth one", ordinals=True), 36.0)
```

Is there a cleaner way to achieve this, or other cases I'm not considering?


Discussion:

Resolving patch:
diff --git a/mycroft/util/lang/parse_en.py b/mycroft/util/lang/parse_en.py
--- a/mycroft/util/lang/parse_en.py
+++ b/mycroft/util/lang/parse_en.py
@@ -138,6 +138,8 @@ def extractnumber_en(text, short_scale=True, ordinals=False):
         "million": 1000000,
         'millions': 1000000}
 
+    string_num_ordinal_en = {}
+
     for num in NUM_STRING_EN:
         num_string = NUM_STRING_EN[num]
         string_num_en[num_string] = num
@@ -147,10 +149,12 @@ def extractnumber_en(text, short_scale=True, ordinals=False):
         if short_scale:
             for num in SHORT_ORDINAL_STRING_EN:
                 num_string = SHORT_ORDINAL_STRING_EN[num]
+                string_num_ordinal_en[num_string] = num
                 string_num_en[num_string] = num
         else:
             for num in LONG_ORDINAL_STRING_EN:
                 num_string = LONG_ORDINAL_STRING_EN[num]
+                string_num_ordinal_en[num_string] = num
                 string_num_en[num_string] = num
 
     # negate next number (-2 = 0 - 2)
@@ -229,6 +233,11 @@ def extractnumber_en(text, short_scale=True, ordinals=False):
         if word in string_num_en:
             val = string_num_en[word]
 
+        # is the prev word an ordinal number and current word is one?
+        # second one, third one
+        if ordinals and prev_word in string_num_ordinal_en and val is 1:
+            val = prev_val
+
         # is the prev word a number and should we sum it?
         # twenty two, fifty six
         if prev_word in sums and word in string_num_en:
@@ -268,6 +277,7 @@ def extractnumber_en(text, short_scale=True, ordinals=False):
 
         else:
             prev_val = val
+
             # handle long numbers
             # six hundred sixty six
             # two million five hundred thousand


Test patch:
diff --git a/test/unittests/util/test_parse.py b/test/unittests/util/test_parse.py
--- a/test/unittests/util/test_parse.py
+++ b/test/unittests/util/test_parse.py
@@ -68,6 +68,9 @@ def test_extract_number(self):
         self.assertEqual(extract_number("this is the third test"), 1.0 / 3.0)
         self.assertEqual(extract_number("this is the third test",
                                         ordinals=True), 3.0)
+        self.assertEqual(extract_number("the fourth one", ordinals=True), 4.0)
+        self.assertEqual(extract_number("the thirty sixth one",
+                                        ordinals=True), 36.0)
         self.assertEqual(extract_number("this is test number 4"), 4)
         self.assertEqual(extract_number("one third of a cup"), 1.0 / 3.0)
         self.assertEqual(extract_number("three cups"), 3)
@@ -518,9 +521,7 @@ def test_multiple_numbers(self):
                          [20, 20, 20, 2])
         self.assertEqual(extract_numbers("third one"),
                          [1 / 3, 1])
-        # NOTE ambiguous case, should return [3] or [3, 1] ?
-        self.assertEqual(extract_numbers("third one", ordinals=True),
-                         [3, 1])
+        self.assertEqual(extract_numbers("third one", ordinals=True), [3])
         self.assertEqual(extract_numbers("six trillion", short_scale=True),
                          [6e12])
         self.assertEqual(extract_numbers("six trillion", short_scale=False),