From 34af78c8534a3081487735a4aab904ddfb1f56cc Mon Sep 17 00:00:00 2001 From: Stephen Waits Date: Fri, 1 Dec 2023 19:45:00 -0700 Subject: [PATCH] refactor(day01): more idiomatic, cleaner, optimal (because fun!) --- src/day01.rs | 62 +++++++++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/src/day01.rs b/src/day01.rs index 52d7740..3afa34f 100644 --- a/src/day01.rs +++ b/src/day01.rs @@ -9,23 +9,18 @@ inventory::submit! { } fn first_and_last_digit(s: &str) -> usize { - // Find the first character in the string that is an ASCII digit - let first = s - .chars() - .find(|c| c.is_ascii_digit()) // Use find to get an Option containing the first digit character - .unwrap() // Unwrap the Option, panicking if no digit is found - .to_digit(10) // Convert the character to a digit in base 10 - .unwrap() as usize; // Convert the digit to a usize + // Find the first ASCII digit in the string + let first = s.chars().filter_map(|c| c.to_digit(10)).next().unwrap() as usize; // Safe to use unwrap as a digit is always present - // Find the last character in the string that is an ASCII digit + // Find the last ASCII digit in the string let last = s .chars() - .rfind(|c| c.is_ascii_digit()) // Use rfind to get an Option containing the last digit character - .unwrap() // Unwrap the Option, panicking if no digit is found - .to_digit(10) // Convert the character to a digit in base 10 - .unwrap() as usize; // Convert the digit to a usize + .rev() // Reverse the iteration for the last digit + .filter_map(|c| c.to_digit(10)) + .next() + .unwrap() as usize; // Safe to use unwrap here as well - // Return the result + // Combine the first and last digits to form a new number first * 10 + last } @@ -35,34 +30,23 @@ lazy_static! { } fn first_and_last_digit_regex(s: &str) -> usize { - // Initialize variables to keep track of the positions and values of the first and last digits. - let mut first_start = usize::MAX; - let mut last_end = 0; - let mut first_digit = String::new(); - let mut last_digit = String::new(); + // Find the first digit in the string by testing against each sice from [0..] to [n-1..] + let first_digit = s + .char_indices() + .find_map(|(index, _)| REGEX.find(&s[index..]).map(|mat| mat.as_str().to_string())) + .unwrap(); // Safe to use unwrap because a digit is always expected - // Iterate over each character index in the string. - for (index, _) in s.char_indices() { - // Check if there is a digit at the current index or beyond. - if let Some(mat) = REGEX.find(&s[index..]) { - // Calculate the start and end positions of the matched digit. - let match_start = index + mat.start(); - let match_end = index + mat.end(); + // Iterate from the end of the string, checking each slice for the last match + let last_digit = s + .char_indices() + .rev() + .find_map(|(index, _)| { + // Check if the slice from this index to the end of the string matches the regex + REGEX.find(&s[index..]).map(|mat| mat.as_str().to_string()) + }) + .unwrap(); // Safe to use unwrap as a digit is always expected - // If the current match starts before any previously found digit, it's the new first digit. - if match_start < first_start { - first_start = match_start; - first_digit = mat.as_str().to_string(); - } - // If the current match ends after any previously found digit, it's the new last digit. - if match_end > last_end { - last_end = match_end; - last_digit = mat.as_str().to_string(); - } - } - } - - // Convert the first and last digit strings to their numeric values and return the result + // Convert the found digits to numbers and calculate the result digit_value(&first_digit) * 10 + digit_value(&last_digit) }