I have multiple conditions. How do i map them out cleanly? 🟠​
A question developers must answer when they have multiple conditions is how do they map the outcomes in a clean way. In this article we will answer that question by using a simple example to check for HTTP response.
- 2xx - Success
- 4xx - Client Error
- 5xx - Server Error
- Anything else - Unknown
To begin with we will go for a classic if-else chain which of course works, but a question lingers what if our codebase grows, will our conditions not grow which may lead us to messy chain of logic which may be hard to maintain. So here we will look at each approach and try to highlight the advantages of each and of course the gaps.
1. Classic If-Else Chain ​
The logic is usually straightforward as it checks one condition at a time. This makes it explicit and easy to follow when there are only a few cases.
function handleResponse(statusCode) {
if (statusCode >= 200 && statusCode < 300) {
return "success";
} else if (statusCode >= 400 && statusCode < 500) {
return "Client Error";
} else if (statusCode >= 500 && statusCode < 600) {
return "Server Error";
} else {
return "Unknown Status";
}
}
console.log(handleResponse(200)); // successAdvantages:
- Simple and readable.
- Reusable as a function.
- Easy to extend in small projects.
Disadvantages:
- Doesn’t scale well — a dozen cases would make this ugly fast.
- Violates the DRY principle (repeats conditions).
- Harder to test or refactor later.
2. Nested Ternary Operator ​
This one puts the condition into one line which is ideal for rendering short logic but not the best option for long logic.
let statusCode = 200;
let handleResponse2 =
(statusCode >= 200 && statusCode < 300) ? "success"
: (statusCode >= 400 && statusCode < 500) ? "Client Error"
: (statusCode >= 500 && statusCode < 600) ? "Server Error"
: "Unknown Status";
console.log(handleResponse2); // successAdvantages:
- Concise.
- Handy in inline expressions such as template literals.
- Does not require a separate function if the usage is small.
Disadvantages:
- Nested ternaries quickly hurt readability.
- Debugging becomes painful if conditions are complex.
- Easy to introduce subtle bugs.
3. If-Else Without a Function ​
This is similar to the one in #1 but does not have a function wrapper thus the code is not reusable.
let handleResponse3;
if (statusCode >= 200 && statusCode < 300) {
handleResponse3 = "success";
} else if (statusCode >= 400 && statusCode < 500) {
handleResponse3 = "Client Error";
} else if (statusCode >= 500 && statusCode < 600) {
handleResponse3 = "Server Error";
} else {
handleResponse3 = "Unknown Status";
}
console.log(handleResponse3); // successAdvantages:
- Quick to write.
- No function overhead.
- Fine for scripts or prototyping.
Disadvantages:
- Not reusable.
- Pollutes scope with extra variables.
- Harder to test and maintain.
4. Switch Statement. ​
This is suitable when your conditions align with ranges or categories.
let handleResponse4;
switch (Math.floor(statusCode / 100)) {
case 2:
handleResponse4 = "success";
break;
case 4:
handleResponse4 = "Client Error";
break;
case 5:
handleResponse4 = "Server Error";
break;
default:
handleResponse4 = "Unknown Status";
}
console.log(handleResponse4); // successAdvantages:
- Very readable for grouped conditions.
- Easier to maintain than a long if-else chain.
- Expresses intent clearly: “check the category.”
Disadvantages:
- Still manual — you must list each group.
- Doesn’t handle irregular cases (e.g., 418 “I’m a teapot”).
5. Rule Table (Simple Strategy Pattern) ​
Here we use object and array of small functions to dictate behavior.Instead of hardcoding conditions, we store rules in data. where each rule has a check (predicate) and a message then the function just finds the first matching rule. This makes it the an ideal option for production code where conditions grow or change often.
const responseMap = [
{ check: code => code >= 200 && code < 300, message: "success" },
{ check: code => code >= 400 && code < 500, message: "Client Error" },
{ check: code => code >= 500 && code < 600, message: "Server Error" }
];
function handleResponse(statusCode) {
const match = responseMap.find(rule => rule.check(statusCode));
return match ? match.message : "Unknown Status";
}
console.log(handleResponse(200)); // success
console.log(handleResponse(404)); // Client Error
console.log(handleResponse(500)); // Server Error
console.log(handleResponse(123)); // Unknown StatusAdvantages
- Extensible: add new rules without touching core logic.
- Data-driven, which means easier to test and maintain.
- Cleaner separation between what the rules are and how they’re applied. Disadantages:
- Slightly abstract for beginners.
- Linear search (.find) — though negligible for small sets.
6. Bucketed Ranges with a Map ​
Here we use Map which is like an object, but it’s better for key–value lookups because; keys can be any type (not just strings), it preserves insertion order and has built-in methods like .get(), .set(), .has() which we can use. We turn the hundreds digit into a key (2xx, 4xx, etc.) thus we are able to do direct lookups.
const responseMap = new Map([
["2xx", "success"],
["4xx", "Client Error"],
["5xx", "Server Error"]
]);
function handleResponse(statusCode) {
const key = Math.floor(statusCode / 100) + "xx";
return responseMap.get(key) || "Unknown Status";
}
console.log(handleResponse(201)); // success
console.log(handleResponse(404)); // Client Error
console.log(handleResponse(503)); // Server ErrorAdvantages:
- Super concise.
- O(1) lookup with Map.
- Very clean for grouped ranges.
Disdvantages:
- Doesn’t handle exceptions inside a bucket unless you special-case them.
- Requires preprocessing (Math.floor(statusCode / 100)).
javascript book
If this interested you, check out my Javascript Book