const A_LCASE: u8 = 97;
pub fn check(candidate: &str) -> bool {
candidate
.bytes()
.filter_map(|c| {
c.is_ascii_alphabetic()
.then(|| 1u32 << (c.to_ascii_lowercase() - A_LCASE))
})
.try_fold(0u32, |ltr_flags, ltr| {
(ltr_flags & ltr == 0).then(|| ltr_flags | ltr)
})
.is_some()
}
This solution uses the ASCII value of the letter to set the corresponding bit position.
First, a const value is set with the ASCII value for a.
-
Since all of the characters are ASCII, they can be iterated with the
bytesmethod. Each byte is iterated as au8, which is an unsigned 8-bit integer, and is passed to the filter_map method. -
The closure inside
filter_mapfirst tests if the byte is_ascii_alphabetic. If so, the byte is passed to thethenmethod, where the byte is setto_ascii_lowercasein its closure. To understand what else is happening inthen, consider the following: -
If the lower-cased letter is subtracted by
a, thenawill result in0, because97minus97equals0.zwould result in25, because122minus97equals25. Soawould have1u32shifted left 0 places (so not shifted at all) andzwould have1shifted left 25 places.
So, for the unsigned thirty-two bit integer (1u32), the value for a would look like
zyxwvutsrqponmlkjihgfedcba
00000000000000000000000000000001
and the value for z would look like
zyxwvutsrqponmlkjihgfedcba
00000010000000000000000000000000
- The
filter mappasses only lowercased ASCII letter bytes converted tou32values to the try_fold method.
// code snipped
.try_fold(0u32, |ltr_flags, ltr| {
(ltr_flags & ltr == 0).then(|| ltr_flags | ltr)
})
.is_some()
}
- The
try_foldhas its accumulator set to au32initialized to0. The closure insidetry_folduses the bitwise AND operator to check if the bit for the letter position has not already been set. - If it has been set, you know the letter is duplicated and
try_foldwill "short circuit" and immediately passNoneto theis_somemethod, which willl returnfalse. - If it has not been set, the bitwise OR operator is used in the
thenmethod to set the bit. If all of the iterations oftry_foldcomplete without finding a duplicate letter (and returningNone), the function returnstruefrom theis_somemethod.
Refactoring
Since a filter_map is used, this approach could be refactored to use filter and a map.
pub fn check_bits(candidate: &str) -> bool {
candidate
.bytes()
.filter(|c| c.is_ascii_alphabetic())
.map(|c| 1u32 << (c.to_ascii_lowercase() - A_LCASE))
.try_fold(0u32, |ltr_flags, ltr| {
(ltr_flags & ltr == 0).then(|| ltr_flags | ltr)
})
.is_some()
}
In benchmarking, this approach was slightly slower, but its style may be preferred.