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
bytes
method. 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_map
first tests if the byte is_ascii_alphabetic. If so, the byte is passed to thethen
method, where the byte is setto_ascii_lowercase
in its closure. To understand what else is happening inthen
, consider the following: -
If the lower-cased letter is subtracted by
a
, thena
will result in0
, because97
minus97
equals0
.z
would result in25
, because122
minus97
equals25
. Soa
would have1u32
shifted left 0 places (so not shifted at all) andz
would have1
shifted 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 map
passes only lowercased ASCII letter bytes converted tou32
values 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_fold
has its accumulator set to au32
initialized to0
. The closure insidetry_fold
uses 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_fold
will "short circuit" and immediately passNone
to theis_some
method, which willl returnfalse
. - If it has not been set, the bitwise OR operator is used in the
then
method to set the bit. If all of the iterations oftry_fold
complete without finding a duplicate letter (and returningNone
), the function returnstrue
from theis_some
method.
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.