Periodic table code golf
Tags: javascript, programming, codegolf
Published: 2022-07-12 21:58:58

The other day I came across this tweet:
{% embed https://twitter.com/CompSciFact/status/1537447575573233666 %}

I thought this was a nice challenge to solve and refresh some programming stuff, so I opened my editor and started coding a solution 🤓

## Understanding the problem
Reading about periodic table in Wikipedia I've discovered that there's [more](https://en.wikipedia.org/wiki/Periodic_table#Alternative_periodic_tables) than one way to display it. Taking into account the example in the tweet, we're going to assume it's the standard form.

If we take a look at the table, the first thing to notice is how elements are disposed in it

![Periodic table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vbrvn4o3sgka7nbcku03.png)

As we can see it's just a regular table but with some exceptions. For instance, helium with an atomic number of 2 should start at group 2, but it doesn't (check [this](https://www.quora.com/Why-is-helium-in-group-18-even-though-it-has-only-2-valence-electrons) if you want to know why), so we need to keep track of these special cases. In total we have 3 exceptions ([He](https://en.wikipedia.org/wiki/Helium), [B](https://en.wikipedia.org/wiki/Boron) and [Al](https://en.wikipedia.org/wiki/Aluminium)).

There's another special case to consider: Lanthanoids and actinoids. For the sake of simplicity we're not going to consider it for this challenge.

Now that everything is clear, let's start!

## Step 1 - 1D to 2D
First task is finding a way to map an atomic number to a row and a column in the table. We know that atomic numbers are sequential and goes from 1 to 118 and also that periodic table has 18 groups. Considering a regular table we could do the following:
```js
function getElementPosition(atomicNumber) {
  const groups = 18
  const index = atomicNumber - 1;
  const row = Math.trunc(index / groups)
  const column = index % groups

  return [row + 1, column + 1]
}
```

Here we're transforming a one dimensional index into a two dimensional one. That's a good starting point but we need to take special cases into consideration

## Step 2 - Special cases
For special cases we can use an array to store when they start and what's the offset we need to apply:
```js
function getElementPosition(atomicNumber) {
  let groups = 18
  let index = atomicNumber - 1;
  let specialNumbers = [
    [2, 16], 
    [5, 10],
    [13, 10],
  ]

  for (let [number, offset] of specialNumbers) {
    if (number <= atomicNumber) {
      index += offset
    }
  }

  let row = Math.trunc(index / groups)
  let column = index % groups

  return [row + 1, column + 1]
}
```

In this case we're adding the required offset based on our special numbers (He, B and Al). We can now do `getElementPosition(2)` and it'll return `[1, 18]` 💪.

But wait a minute, what happens with gold? Calling `getElementPosition(79)` returns `[7, 7]`. Nope, something is definetively not working

## Step 3 - Lanthanoids and actinoids
Ok, we didn't thought of lanthanoids and actinoids. Let's try to fix it with our special cases array:
```js
function getElementPosition(atomicNumber) {
  let groups = 18
  let index = atomicNumber - 1;
  let specialNumbers = [
    [2, 16], 
    [5, 10],
    [13, 10],
    [71, -14],
    [103, -14],
  ]

  for (let [number, offset] of specialNumbers) {
    if (number <= atomicNumber) {
      index += offset
    }
  }

  let row = Math.trunc(index / groups)
  let column = index % groups

  return [row + 1, column + 1]
}
```

In this case instead of expanding (positive offset) we're collapsing (negative offset). Yes, it works now, calling `getElementPosition(79)` returns `[6, 11]` 🙂.

Almost there, let's go to final step

## Step 4 - Minification
After using a [minification service](https://skalman.github.io/UglifyJS-online/) the code looks like this:

```js
function getElementPosition(t){let e=t-1,n=[[2,16],[5,10],[13,10],[71,-14],[103,-14]];for(let[o,r]of n)o<=t&&(e+=r);return[Math.trunc(e/18)+1,e%18+1]}
```

**150** bytes characters in total (far below 280 for a tweet). Not bad but we can still short it a bit:

```js
g=(e=>{let r=e-1,t=[[2,16],[5,10],[13,10],[71,-14],[103,-14]];for(let[f,l]of t)f<=e&&(r+=l);return[~~(r/18)+1,r%18+1]});
```

By using arrow function syntax and changing `Math.trunc` to `~~` we've trimmed it to just **120** characters!

## Conclusion
This was a great challenge, nothing too hard but difficult enough to train our brains. And we also have learned some interesting thing about this famous table 👨‍🔬

Cover image source https://www.flickr.com/photos/mindfrieze/258644767