Dig Deeper
regex match dupe
Regular expression to match a duplicate letter
export function isIsogram(word) {
return !/([a-z]).*?\1/i.test(word);
}
In the next part of the pattern .*?:
. looks for another character which is not a line terminator.
* looks for zero or more of those characters.
? looks for as few of those characters as possible, until
\1 finds a repeat of the first captured character.
If no repeat is found for the first captured character,
then the regex tries again and matches the next a-z as the first captured character, and so on.
The i at the end of the pattern means to ignore case when matching, so [a-z] will also find [A-Z].
The ! at the beginning of the pattern is the logical NOT operator.
The function returns true if the regex does not find a repeated letter.
filter with Set
filter with Set
export function isIsogram(string) {
let word = [...string.toLowerCase()].filter(
(letter) => letter >= 'a' && letter <= 'z',
);
return new Set(word).size == word.length;
}
With this approach you will instantiate a Set of the used letters and compare its size with the filtered word length.
- First, the lowercased input string is made into an Array of its characters using spread syntax.
- The
filter method is then called on the Array to filter out any character that is not a-z.
- The letters that survive the filter are assigned to
word.
- A
Set is initialized from the letters in word.
The size of unique letters is compared with the length of the filtered word length.
If the number of unique letters in the Set of used letters is the same as the number of filtered letters,
then the function returns true.
Bit field
Bit field
const A_LCASE = 97;
const A_UCASE = 65;
export function isIsogram(word) {
let letter_flags = 0;
for (const letter of [...word]) {
if (letter >= 'a' && letter <= 'z') {
if ((letter_flags & (1 << (letter.charCodeAt(0) - A_LCASE))) != 0)
return false;
else letter_flags |= 1 << (letter.charCodeAt(0) - A_LCASE);
} else if (letter >= 'A' && letter <= 'Z') {
if ((letter_flags & (1 << (letter.charCodeAt(0) - A_UCASE))) != 0)
return false;
else letter_flags |= 1 << (letter.charCodeAt(0) - A_UCASE);
}
}
return true;
}
This solution uses the ASCII value of the letter to set the corresponding bit position.
Some constants are defined for readability in the function.
The ASCII value for a is 97.
The ASCII value for A is 65.
- Spread syntax is used to make an
Array of the characters in the word.
- The
string loops through its characters and looks for a character being a through z or A through Z.
- If a letter is found, then its ASCII value is taken by the
charCodeAt method.
`charCodeAt` actually returns the UTF-16 code unit for the character, which is an integer between `0` and `65535`.
For the letters `a`-`z` and `A`-`Z`, the UTF-16 number is the same value as the ASCII value.
- If the lowercase letter is subtracted by
97, then a will result in 0, because 97 minus 97 equals 0.
z would result in 25, because 122 minus 97 equals 25.
So a would have 1 shifted left 0 places (so not shifted at all) and z would have 1 shifted left 25 places.
- If the uppercase letter is subtracted by
A, then A will result in 0, because 65 minus 65 equals 0.
Z would result in 25, because 90 minus 65 equals 25.
So A would have 1 shifted left 0 places (so not shifted at all) and Z would have 1 shifted left 25 places.
In that way, both a lower-cased z and an upper-cased Z can share the same position in the bit field.
So, for an unsigned thirty-two bit integer, if the values for a and Z were both set, the bits would look like
zyxwvutsrqponmlkjihgfedcba
00000010000000000000000000000001
We can use the bitwise AND operator to check if a bit has already been set.
If it has been set, we know the letter is duplicated and we can immediately return false.
If it has not been set, we can use the bitwise OR operator to set the bit.
If the loop completes without finding a duplicate letter (and returning false), the function returns true.
Source: Exercism javascript/isogram