The other day I came across this tweet:
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 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
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 if you want to know why), so we need to keep track of these special cases. In total we have 3 exceptions (He, B and Al).
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:
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:
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:
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 the code looks like this:
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:
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
Top comments (0)