Refactoring Wars Part 1. The Hash Map Menace

NB! This article contains actually useful example of hash maps in TS! If you are here for the knowledge, just wait for it. It will come.

Codemedian
6 min readDec 22, 2021
A long time ago in a galaxy far, far away....
Jedis having an epic battle

Great code masters came to ways to produce spaceships with Typescript code and nothing else (well, what do you expect, some trends don’t change). But the Dark Lords were ahead of good forces: almighty Dart Vader using the power of Dart with his mighty allies created a codebase which is way more consistent, relies on good design patterns and uses full power of OOP. They could generate 4x times more battleships and drones, taking over the Galaxy and Federation, last hopes were fading out and all the living beings were preparing to live under Dart Vader’s tyranny. But Jedis were still strong, although exhausted from constant war and almost out of the coffee. The unknown warrior, invited by Master Yoda himself, had entered the castle of the planet with a strangest name i_dont_care_about_the_planet_name_haha. The Jedis gathered together in the conference room, desperate to hear some thoughts on how to optimise their codebase to compete with Dar(t)k Forces.

All Jedi Devs gathered for Architectural meeting

‘Your name shall Dev Jedis hear’, greeted the newcomer Master Yoda.

‘My name is not important’, answered stranger with hesitation in his voice.

‘Tell us your name, you fool! Or I will hack your PC and find it out myself’, said the fattest of Jedi Devs with a very angry voice.

‘It’s Kevin’, he answered. ‘Kevin, the Coder in the Night.

‘I’m George, the member of Sweaty Armpit family, hacker of Amazon planet. Tell us the purpose of your visit’, casually said the fattest Jedi Dev.

‘I came to tell you about hash maps, my lord’, answered Kevin. ‘I’ve spent three years meditating and coding in caves when sudden vision of a hash map came to me. It was a divine spark of light in these very dark times’ .

‘Good job you did, Kevin’, kindly said Yoda. ‘But why the powers of Google you did not use? As might hash map be already existing in the Web!’

The awkward silence filled the conference room.

‘F***, whispered Kevin to himself, then turned his head to Yoda. ‘As I did not have such wisdom as you do, master Yoda’.

‘Anyway, what are those hash maps?’, interestedly asked Sweat Butt George.

‘I will show you on the example from your codebase. Here, you have functionality for adding weapons to spaceships. Here we have an interface for spaceShip:

interface ISpaceShip {
shipType: ShipType;
weapons: IWeapon[];
}

Spaceship can be three types: big transporter ship, smaller reactive ship and standard ship which is something in the middle:

type ShipType = 'transporter' | 'reactive' | 'standard';

Every ship has array of weapons. Weapons also have different types and positions:

interface IWeapon {
position: WeaponPosition;
weaponType: WeaponType;
}

There are only two positions for weapons: left or right:

type WeaponPosition = 'left' | 'right';

The weapon itself can be either laser or a gun:

type WeaponType = 'gun' | 'laser';

So, here we have 4 predefined weapons we want to add to the spaceships:

const leftGun: IWeapon = {position: 'left', weaponType: 'gun'};
const rightGun: IWeapon = {position: 'right', weaponType:'gun'};
const leftLaser: IWeapon = {position: 'left', weaponType: 'laser'};
const rightLaser: IWeapon = {position: 'right', weaponType:'laser'};

And here are spaceships without yet inserted weapons:

const transporterShip: ISpaceShip = {shipType: 'transporter', weapons: []};
const reactiveShip: ISpaceShip = {shipType: 'reactive',
weapons: []};
const standardShip: ISpaceShip = {shipType: 'standard',
weapons: []};

Of course, we also want methods for inserting predefined guns to spaceShips:

const addLeftGun = (spaceShip: ISpaceShip) => {
spaceShip.weapons.push(leftGun);
}

const addRightGun = (spaceShip: ISpaceShip) => {
spaceShip.weapons.push(rightGun);
}

const addLeftLaser = (spaceShip: ISpaceShip) => {
spaceShip.weapons.push(leftLaser);
}

const addRightLaser = (spaceShip: ISpaceShip) => {
spaceShip.weapons.push(rightLaser);
}

At this point, we are full packed to create a method which decides which guns should be added depending on the spaceship type. And here, Jedis, how this method looks so far:

const addWeaponToSpaceShip = (spaceShip: ISpaceShip) => {
if (spaceShip.shipType === 'transporter') {
addLeftGun(spaceShip);
addRightGun(spaceShip);
addLeftLaser(spaceShip);
addRightLaser(spaceShip);
}
else if (spaceShip.shipType === 'reactive') {
addLeftLaser(spaceShip);
addRightLaser(spaceShip);
}
else if (spaceShip.shipType === 'standard') {
addLeftGun(spaceShip);
addRightGun(spaceShip);
}
}

After running for example addWeaponToSpaceShip(reactiveShip), the result will look like this:

{ shipType: 'reactive',
weapons: [
{
position: "left",
weaponType: "laser"
},
{
position: "right",
weaponType: "laser"
}
]
}

So, overall the implementation is this: we want to add a full pack of guns to transporter ship. For reactive ships, we want only lasers as they are fast pilots and shooters. For standard spaceships, we need only guns to cut our expenses and still provide more war force. Am I right, Master Yoda?’

‘Indeed, right you are, dear Kevin’, answered Yoda.

‘So, what problem do you see?’, asked Sweaty Armpit George.

‘So, if the standard ship runs through this function’, continued Kevin. ‘We will have our execution starting only on 11nth line. And most of our spaceships are standard so they will go through bunch of if statements before reaching the target. But firstly, let’s look at the transporter adding weapons implementation. It’s four methods to add something which is an array of weapons which we can predefine from the start as for every other ship type! Let’s create this arrays of weapons for every ship type and define them instead and add them to weapon array using only one method:

const transporterShipWeapons: IWeapon[] = 
[leftGun, rightGun, leftLaser, rightLaser];
const reactiveShipWeapons: IWeapon[] = [leftLaser, rightLaser];
const standardShipWeapons: IWeapon[] = [leftGun, rightGun];

And, let’s create a method to insert all the weapons from the array to the spaceShip:

const insertWeapons = (weapons: IWeapon[], spaceShip: ISpaceShip) => {
weapons.forEach((weapon: IWeapon) => { spaceShip.weapons.push(weapon));
}
}

And refactor our main method addWeaponToSpaceShip. which will now look like this:

const addWeaponToSpaceShip = (spaceShip: ISpaceShip) => {
let weapons: IWeapon[];
if (spaceShip.shipType === 'transporter') {
weapons = transporterShipWeapons;
}
else if (spaceShip.shipType === 'reactive') {
weapons = reactiveShipWeapons;
}
else if (spaceShip.shipType === 'standard') {
weapons = standardShipWeapons;
}
insertWeapons(weapons, spaceShip);
}

It gives the same results but for each if statement we have a single line of code which defines what kind of weapons this spaceship will have. In the end, we just insert them.’

‘Impressed am I, young master’, said Yoda. ‘Are you calling this hash maps?’

‘No’, answered Kevin shining out of proudness. ‘Hash map is a data structure that holds key-value pairs and reduces our code implementation to O(1) complexity. With this example, only few changes are needed. Let’s create a map that uses ship types as keys and desired weapons array as value:

const weaponMap: Map<ShipType, IWeapon[]> = new Map([
['transporter', transporterShipWeapons],
['reactive', reactiveShipWeapons],
['standard', standardShipWeapons],
]);

The Map object has different methods but the one that we need here is get(). By calling get with the key value, we will receive desired array of weapons for our ship. Let’s implement this in our code:

const addWeaponToSpaceShip = (spaceShip: ISpaceShip) => {
const weapons: IWeapon[] = weaponMap.get(spaceShip.shipType);
insertWeapons(weapons, spaceShip);
}

So, by this point we reduced our method to two lines of code and our inserting weapons functionality to one method instead of four. And it works exactly as it worked before but it’s far more optimised!’.

Yoda and Jedis were shocked. How come they did not know that? Sweaty Armpit George was sweating even more than before but straight away commanded devs to start big refactoring process to their code base. In a few weeks, their code looked far better than before, it was more performant and devs started to add new features faster, all thanks to Kevin and hash maps. The good forces had chance again to fight back and Kevin…well, he learned his lesson too and did not go back to caves but started living on i_dont_care_about_the_planet_name_haha (that’s the planet name, if you don’t remember) with a good internet connection so he could utilise Google powers to maximum. Kevin started to learn and learn fast. With him, the Federation finally obtained hope to fight the Dart Vader back! But what will be the result of this epic battle? Coming soon….

Here is a snippet of Kevin’s code that I received by pure chance:

https://github.com/wazebase/typescript-hash-map-example/tree/master

If you liked this space coding saga and you want a second one about Factories, write me in the comments :)

--

--

Codemedian

I am Code Median, I write funny and useful things about frontend development.