Elements: Decompose Words into their Chemical Ingredients

01.10.24    web technologies    website

I've created a small web tool that shows you the chemical composition of any substance! Well, it shows the chemical composition of its name at least. And with "chemical composition" I just mean combination of symbols (i.e. one or two-letter code names) of chemical elements. I've embedded right here into the blog. So play around:

Elements

Explanation

The input word is analyzed as a string of letters which is decomposed into sequences of chemical element symbols. For example the input word "nature" becomes N-At-U-Re, that is Nitrogen-Astatine-Uranium-Rhenium. The word "nice" gets the sequence Ni-Ce: Nickel-Cerium. But it also gets N-I-Ce: Nitrogen-Iodine-Cerium. In such an ambiguous case, my tool finds all combinations displays them. And of course, some — well many — words cannot be fully decomposed at all and have no sequence of chemical elements displayed.

In my implementation the solutions are calculated using a recursive function like this:

// Calculate the list of solutions
// each being a list of symbols
// for a given input string:
function calculate(word) {
  // Recursive base case:
  // The empty word has a single trivial solution.
  if(word.length == 0) return [[]];
  // Otherwise, we have a list of solutions.
  const result = [];
  // For possible lenghts of the first symbol:
  // (Chemical symbols only have length 1 or 2.)
  for(let k of [2, 1]) {
    if(k > word.length) continue;
    const key = word.substring(0, k).toLowerCase();
    const lookup = symbols[key];
    // Is the k-prefix of the word a chemical symbol?
    if(lookup) {
      // Find the solutions for the rest
      // of the input word recursively
      let subresult = calculate(word.substr(k));
      // Prepend the first symbol to each solution,
      // and add them to this word's solutions.
      result.push(...subresult.map(x => [lookup, ...x]));
    }
  }
  return result;
}

This problem has the look and feel of classic textbook computer science problems. Consequently, there is room for improvement to my simple approach. Recursive calls in my approach can be highly redundant and could be elided with memoization and dynamic programming. But from a CS perspective it is not easy to make a meaningful improvement because we are interested in finding and displaying all solutions1. If we only wanted to find any single solution (if it exists), then such improvements would easily be significant. In any case, my implementation is just fine in practice because it responds nearly instantly as-is, and I don't expect the workload for my fun demo increase significantly.

Technology

For the interactive frontend2 code implemented here, I have employed the new UI library Alpine.js. My small project was also meant to be an experiment for testing this library's usage. Alpine feels like a simplified version of vue.js. Specifically, it gives you vue's templating capabilities while still being just a library instead of a whole framework. It is used without a compilation step whereas such a workflow is not recommended with vue. This makes Alpine feel nice and easy, and thus it's perfect fit for simple interactions like this.


  1. Here "meaningful" implies a Big-O-level improvement to the worst-case time complexity. For example, in the word "cocococo", where each syllable can be decomposed into either Carbon-Oxygen or just Cobalt, the final syllable is analyzed very often — exponentially often if you keep adding more "co" syllables. But there will also be exponentially many solutions, meaning my implementation is already optimal for time complexity. 

  2. Well, there is no "backend" to this "frontend". Here the word "frontend" is used as the established term for code-running-in-the-browser.