DEV Community

Cover image for Building a Five-Letter Word Filter with Vanilla JavaScript
Kamran Ali
Kamran Ali

Posted on

Building a Five-Letter Word Filter with Vanilla JavaScript

Word-pattern filters are useful for word games, vocabulary tools, and small text-processing projects. In this tutorial, we will build a simple five-letter word filter using plain JavaScript without a framework or external API.

The filter will support three conditions:

  • A five-character pattern using ? for unknown positions
  • Letters that must occur in the word
  • Letters that must not occur in the word

Creating a Sample Word List

We will start with a small array of five-letter words:

const words = [
  "brain",
  "brave",
  "bread",
  "chase",
  "crane",
  "crate",
  "crave",
  "dream",
  "flame",
  "grace",
  "grain",
  "place",
  "plane",
  "share",
  "stone"
];
Enter fullscreen mode Exit fullscreen mode

A production tool would normally use a much larger and carefully validated word list, but this sample is enough to demonstrate the filtering logic.

Matching a Word Pattern

Suppose the user enters this pattern:

c?a?e
Enter fullscreen mode Exit fullscreen mode

The known positions are:

  • c in the first position
  • a in the third position
  • e in the fifth position

Each ? represents an unknown letter.

We can compare a word with the pattern using every():

function matchesPattern(word, pattern) {
  return [...pattern].every((character, index) => {
    return character === "?" || word[index] === character;
  });
}
Enter fullscreen mode Exit fullscreen mode

Calling the function with crane returns true:

matchesPattern("crane", "c?a?e");
Enter fullscreen mode Exit fullscreen mode

However, a word such as chase returns false because its letter positions do not satisfy the complete pattern.

Checking Required Letters

Required letters must occur somewhere in the candidate word. Their positions do not need to be known.

function hasRequiredLetters(word, requiredLetters) {
  return requiredLetters.every((letter) => word.includes(letter));
}
Enter fullscreen mode Exit fullscreen mode

Before calling the function, we can convert the user’s input into an array:

const requiredLetters = [...new Set("ar".toLowerCase())];
Enter fullscreen mode Exit fullscreen mode

Using Set prevents the same letter from being checked unnecessarily when it is entered more than once.

Removing Excluded Letters

The excluded-letter condition works in the opposite way. A candidate is valid only when none of the excluded letters occur in it.

function avoidsExcludedLetters(word, excludedLetters) {
  return excludedLetters.every((letter) => !word.includes(letter));
}
Enter fullscreen mode Exit fullscreen mode

For example:

const excludedLetters = [...new Set("bts".toLowerCase())];
Enter fullscreen mode Exit fullscreen mode

Any word containing b, t, or s will now be removed.

Combining the Conditions

We can combine all three checks inside one filtering function:

function filterWords(words, pattern, required, excluded) {
  const normalizedPattern = pattern.toLowerCase().trim();
  const requiredLetters = [...new Set(required.toLowerCase())];
  const excludedLetters = [...new Set(excluded.toLowerCase())];

  if (!/^[a-z?]{5}$/.test(normalizedPattern)) {
    throw new Error(
      "The pattern must contain exactly five letters or question marks."
    );
  }

  return words.filter((word) => {
    return (
      matchesPattern(word, normalizedPattern) &&
      hasRequiredLetters(word, requiredLetters) &&
      avoidsExcludedLetters(word, excludedLetters)
    );
  });
}
Enter fullscreen mode Exit fullscreen mode

We can now test the filter:

const matches = filterWords(
  words,
  "c?a?e",
  "r",
  "bts"
);

console.log(matches);
Enter fullscreen mode Exit fullscreen mode

The resulting array contains words that satisfy every condition.

Handling User Input Safely

Inputs should be normalized before filtering. Spaces, numbers, punctuation, and other unexpected characters can be removed from letter fields:

function cleanLetters(value) {
  return value.toLowerCase().replace(/[^a-z]/g, "");
}
Enter fullscreen mode Exit fullscreen mode

Clear validation messages should also be displayed when:

  • The pattern contains fewer or more than five characters
  • Unsupported characters are entered
  • No words satisfy the selected conditions

This gives users useful feedback instead of silently returning an empty result.

Performance

The filter checks each word once, so its basic time complexity is O(n), where n is the number of words in the dataset. This is efficient enough for a typical browser-based word list.

For much larger datasets, possible improvements include:

  • Pre-grouping words by length
  • Creating indexes for starting and ending letters
  • Caching repeated searches
  • Loading data only when required
  • Moving intensive processing to a Web Worker

Next Steps

This basic implementation can be extended with fixed-position exclusions, repeated-letter rules, sorting, frequency information, and scoring systems.

If you want to compare the JavaScript approach with a working interface containing more filtering options, the WordHubPro five-letter word finder provides an interactive example.

The most important lesson is to keep each filtering condition separate. Small, focused functions are easier to understand, test, and improve than one large block of filtering logic.

Top comments (0)