I'm trying to create a Rust version of the accepted solution to this question, which is to convert a string such as "two hundred fifty eight" into 258.
I created the code below to match the pseudocode given, but somehow it's unable to get the hundreds and thousands right. My example string returns 158 instead of 258, for instance. What am I missing?
let _units = vec![ "zero", "one", "two", "three", "four", "five", "six", "seven",
"eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen", ];
let mut numbers: Vec<(&str,u32)> = _units.iter().enumerate().map(|(idx, n)| (*n, idx as u32)).collect();
let _tens = vec!["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"];
let mut tens: Vec<(&str,u32)> = _tens.iter().enumerate().map(|(idx, n)| (*n, (idx * 10) as u32)).collect();
let _scales = vec!["hundred", "thousand", "million", "billion"];
let base:i32 = 1000;
let mut scales: Vec<(&str,u32)> = _scales.iter().enumerate().map(|(idx, n)| (*n, base.pow(idx as u32) as u32)).collect();
numbers.append(&mut tens);
numbers.append(&mut scales);
use std::collections::HashMap;
fn text_to_int(textnum: &str, numwords: HashMap<&str, u32>) -> u32 {
let mut result = 0;
let mut prior = 0;
for word in textnum.split(" ") {
let value = numwords[word];
if prior == 0 {
prior = value;
} else if prior > value {
prior += value;
} else {
prior *= value;
};
if value > 100 && prior != 0 {
result += prior;
prior = 0;
};
}
return result + prior;
}
let numwords: HashMap<_, _> = numbers.into_iter().collect();
let textnum = "two hundred fifty eight thousand";
println!("{:?}", text_to_int(textnum, numwords));
Returns 158000. What am I doing wrong?
your problem is with this line
let base:i32 = 1000;
let mut scales: Vec<(&str,u32)> = _scales.iter().enumerate().map(|(idx, n)| (*n, base.pow(idx as u32) as u32)).collect();
because "hundred" give you 1 not 100
with your algorithm you can't produce correct numbers for scales
"hundred" -> 100 -> 10^2
"thousand" -> 1_000 -> 10^3
"million" -> 1_000_000 -> 10^6
"billion" -> 1_000_000_000 -> 10^9
simple way to fix this is add "hundred" manually
let _scales = vec!["thousand", "million", "billion"];
let base:i32 = 1000;
let mut scales: Vec<(&str,u32)> = _scales.iter().enumerate().map(|(idx, n)| (*n, base.pow(idx as u32 + 1) as u32)).collect();
numbers.append(&mut tens);
numbers.append(&mut scales);
numbers.push(("hundred", 100));
[Edit]
Alternative way to solve this question is work with FromStr
that will give you advantages of parse()
function
for do this you can use following code
first create a custom type for holding value such as u32
#[derive(Debug)]
struct Number<T>(T);
its' good if you impl Display
too (Optional)
impl<T: Display> Display for Number<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
now create text number map
const BASIC: [(&'static str, u32); 32] = [
("zero", 0),
("one", 1),
("two", 2),
("three", 3),
("four", 4),
("five", 5),
("six", 6),
("seven", 7),
("eight", 8),
("nine", 9),
("ten", 10),
("eleven", 11),
("twelve", 12),
("thirteen", 13),
("fourteen", 14),
("fifteen", 15),
("sixteen", 16),
("seventeen", 17),
("eighteen", 18),
("nineteen", 19),
("twenty", 20),
("thirty", 30),
("forty", 40),
("fifty", 50),
("sixty", 60),
("seventy", 70),
("eighty", 80),
("ninety", 90),
("hundred", 100),
("thousand", 1000),
("million", 1000000),
("billion", 1000000000),
];
now just place your text_to_int
function codes in FromStr
function like this
impl FromStr for Number<u32> {
type Err = ();
fn from_str(textnum: &str) -> Result<Self, Self::Err> {
let textnum = textnum.to_lowercase();
let numwords: HashMap<_, _> = BASIC.into_iter().collect();
let mut result = 0u32;
let mut prior = 0u32;
for word in textnum.split(" ") {
let value = numwords[word].into();
if prior == 0 {
prior = value;
}
else if prior > value {
prior += value;
}
else {
prior *= value;
}
if value > 100 && prior != 0 {
result += prior;
prior = 0;
}
}
Ok(Number(result + prior))
}
}
now you can parse every &str
like this
fn main() {
let num: Number<u32> = "two hundred fifty eight thousand".parse().unwrap();
println!("{}", num);
}