Breaking the "switch (true)" Spell in JavaScript
Catherine Östlund
Posted on May 22, 2023
In the mystical realm of JavaScript, enchanting constructs known as switch statements are often employed to guide the flow of code based on different conditions. Switch statements offer a reliable and concise way to handle multiple cases, making code easier to navigate. However, a mysterious variant known as switch (true)
has emerged, wielding its own powers and luring developers into it's hold. Join me on a journey as we together unravel the secrets behind switch (true)
and explore alternative paths to write cleaner and more maintainable code.
Understanding switch (true)
Switch (true)
is a variation of the traditional switch statement in JavaScript that allows for evaluating multiple conditions and executing multiple cases. Instead of specifying a specific expression in the parentheses, switch (true)
uses a boolean expression that determines which case to carry out depending on if the expression evaluates as truthy or not.
Consider the following case:
You are working on a cart-component for an e-commerce site and you start working on something like this, using switch (true)
to apply discounts to products in a shopping cart based on their quantity and category:
const cart = [
{ id: 1, name: 'Smartphone', category: 'electronics', price: 500, quantity: 2 },
{ id: 2, name: 'T-Shirt', category: 'clothing', price: 20, quantity: 5 },
{ id: 3, name: 'Throw Pillow', category: 'home goods', price: 15, quantity: 1 },
];
let total = 0;
// Apply discount based on quantity or category
cart.forEach(product => {
switch (true) {
case product.quantity >= 5:
total += product.quantity * product.price * 0.7; // 30% discount
break;
case product.category === 'electronics':
total += product.quantity * product.price * 0.9; // 10% discount
break;
case product.category === 'clothing':
total += product.quantity * product.price * 0.8; // 20% discount
break;
default:
total += product.quantity * product.price; // No discount
}
});
console.log({total}); // Output: { total : 985 }
In this example, the switch (true)
evaluates multiple conditions (quantity and category) for each product and executes the corresponding case to apply the appropriate discount. Clean and simple, right?
The Problems
Well, at first glance, using switch (true)
might feel like a natural choice because it feels like you need to write less code for each new condition (I know I've fallen for it too...). It also might take up fewer lines than say, using multiple if-else statements (also prone to becoming cluttered and difficult).
However, if you start looking closer it actually hinder readability and can prevent the code from having a clear structure. Also, to understand what the switch is actually doing you have to study each line, each condition and each outcome and this takes time. Precious time that might have been spent implementing that cool new feature you've been thinking about.
Imagine the previous example where we might also start combining discounts:
cart.forEach(product => {
switch (true) {
case product.quantity >= 5:
if (product.category === 'electronics') {
total += product.quantity * product.price * 0.7 * 0.9; // 30% discount + 10% discount for electronics
} else {
total += product.quantity * product.price * 0.7; // 30% discount
}
break;
case product.category === 'electronics':
total += product.quantity * product.price * 0.9; // 10% discount
break;
case product.category === 'clothing':
total += product.quantity * product.price * 0.8; // 20% discount
break;
default:
total += product.quantity * product.price; // No discount
}
});
As we can see, the use of switch (true)
in this scenario leads to several issues:
Lack of Clarity
The expression within the switch statement is not very clear or explicit, which can make it hard to understand what the code is checking for.Complexity and Readability
As the number of categories and discounts increases, the switch statement can become difficult to read and maintain.Limited Debugging and Error Handling
Debugging the switch statement becomes harder as it's not clear which case is being executed.
So, what do we do instead?
Alternatives and Best Practices
Just as with everything when it comes to coding, there are several ways of tackling these problems. One way could be to use traditional switch-statements with specific expressions, utilizing if-else statements for clearer condition handling or maybe exploring some more of JavaScript's native array methods like Array.find()
or Array.reduce()
. Or any combination of them. The important thing is to keep the codes purpose clear, easy to navigate and simple to build on.
Let's start by looking at an alternative using traditional switch-statements and if clauses:
cart.forEach(product => {
if (product.quantity >= 5) {
total += product.quantity * product.price * 0.7; // 30 % discount
return;
}
switch (product.category) {
case 'electronics':
total += product.quantity * product.price * 0.9; // 10% discount
break;
case 'clothing':
total += product.quantity * product.price * 0.8; // 20% discount
break;
default:
total += product.quantity * product.price; // No discount
}
});
In this example, the switch is used to only apply discounts based on the category of each product. This makes the code more explicit and easier to follow, improving code readability.
Another approach could be to use objects and helper functions and array methods to refactor the code and make it a bit more modular and scalable:
const discounts = {
electronics: 0.9, // 10% discount
clothing: 0.8, // 20% discount
};
const calculateDiscount = product => {
if (product.quantity >= 5) {
return 0.7; // 30 % discount if you buy 5 or more
}
return discounts[product.category] || 1; // If category not found, no discount
}
const total = cart.reduce((acc, product) => {
const discount = calculateDiscount(product);
return acc + product.price * discount * product.quantity;
}, 0);
console.log({total}); // Output: { total : 985 }
Here we are using an object to define the categories and their corresponding discounts followed by the reduce() method to apply the discounts to the total price of the products in the cart. Just like in our previous example this solution provides more explicitness and improves code readability and maintainability while remaining quite clean and clear.
Conclusion
In our journey, we've seen that Switch statements are reliable tools in JavaScript that help us navigate code based on different conditions, but the variant switch(true) brings unwanted complexity, reduced readability, and limited debugging capabilities. Embracing traditional switch statements, if-else structures, or native JavaScript methods provides clarity and maintainability, freeing us from the temptation of switch(true) and enabling more elegant solutions.
Remember the importance of prioritising code readability and maintainability. This helps us foster collaboration among developers and ensure the longevity of our codebase and guides us to a future where our JavaScript code shines brightly, illuminating the way for all who follow.
So, fellow developers and recovering victims of the switch(true), let's always keep growing and learning! The power of clean, maintainable code awaits us, ready to empower our development endeavours and lead us to success.
Posted on May 22, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.