javascript

You don’t actually need if statements (EVER)

Sure they’re a nice and easy way to create control flow, but you can write many billions of lines of conditional JS code without a SINGLE if statement.

And there are many situations where a different construct shows what you wanna do way more clearly — something we can’t ignore as long we write code for humans. Not to mention lower verbosity and shorter code.

So: let’s look at some powerful if statement upgrades.

1. The AND (&&) operator

The && operator, unique to JavaScript.

We it I quickly go from this:

JavaScript
function visitSite(user) { if (user.isLoggedIn) { console.log(`You are ${user.name}`); } console.log('Welcome to Coding Beauty.'); }

To this:

JavaScript
function visitSite(user) { user.isLoggedIn && console.log(`You are ${user.name}`); console.log('Welcome to Coding Beauty.'); }

I’ve eradicated the nested and compacted the branching logic into a one-liner.

You want to use this when there’s an if but no matching else; especially when the if block has only one line.

Even if there are multiple lines you can abstract them into a separate function and apply && again. After all the console.log() in our example is an abstraction itself.

So this:

JavaScript
function visitSite(user) { if (user.isLoggedIn) { console.log(`Welcome back, ${user.name}!`); console.log( `Your last login was on ${user.lastLoginDate}` ); console.log(`Your account type is ${user.accountType}`); } console.log('Welcome to Coding Beauty.'); }

Transforms to this:

JavaScript
function visitSite(user) { user.loggedIn && handleUser(user); console.log('Welcome to Coding Beauty.'); } function handleUser(user) { console.log(`Welcome back, ${user.name}!`); console.log( `Your last login was on ${user.lastLoginDate}` ); console.log(`Your account type is ${user.accountType}`); }

2. Ternary operator

Ternary operators let us compact if-else statements into a one-liner.

They’re great if-else replacements when all conditional cases only involve assigning a value to the same variable.

Here’s an example:

JavaScript
let word; if (num === 7) { word = 'seven'; } else { word = 'unknown'; }

Even though the DRY principle isn’t a hard and fast rule, for this instance things will be much cleaner if we used ternaries to avoid writing the variable assignment twice:

JavaScript
const word = num === 7 ? 'seven' : 'unknown';

Now we can even use const to keep things immutable and pure.

Nested ternaries

And when we have 3 or more branches in the if-else statement or we nest ifs, the cleaner ternary replacement will contain inner ternaries.

So this:

JavaScript
const getNumWord = (num) => { if (num === 1) { return 'one'; } else if (num === 2) { return 'two'; } else if (num === 3) { return 'three'; } else if (num === 4) { return 'four'; } else return 'unknown'; };

Becomes:

JavaScript
const getNumWord = (num) => num === 1 ? 'one' : num === 2 ? 'two' : num === 3 ? 'three' : num === 4 ? 'four' : 'unkwown';

Some people do cry about nested ternaries though, arguing that they’re complicated and cryptic. But I think that’s more of a personal preference, or maybe poor formatting.

And it’s formatting, we have tools like Prettier that have been doing this job (and only this job) for centuries.

Ever had code this badly formatted?

As long as there’s sufficient indentation you should have no problems with readability. If the branching gets much more complex than above you probably want to move some of the lower-level logic into preceding variables or tiny functions.

Speaking of readability, Prettier is changing their nested ternary style; they’ve come up with something quite unique.

The current style uses a clever combination of flat and tree-like nesting; adding further indentation for nested ternaries in the truthy branch, but keeping things flat for those in the in the falsy branch.

JavaScript
const animalName = pet.canSqueak() ? 'mouse' : pet.canBark() ? pet.isScary() ? 'wolf' // Only nests this because it's in the truthy section : 'dog' : pet.canMeow() ? 'cat' : pet.canSqueak() // Flat because it's in the falsy section ? 'mouse' : 'probably a bunny';

But very soon Prettier will format the above like this:

JavaScript
const animalName = pet.canSqueak() ? 'mouse' : pet.canBark() ? pet.isScary() ? 'wolf' : 'dog' : pet.canMeow() ? 'cat' : pet.canSqueak() ? 'mouse' : 'probably a bunny';

The main change is the ?‘s are all now at the ending of the same line of its ending, instead of the next one.

3. Switch statement

You will find this in C-style languages like Java, C#, and Dart — and it looks exactly the same in those languages with a few semantic differences.

If ternaries are best for generating output for one variable, then switch statements are best for processing input *from* one variable:

JavaScript
function processUserAction(action) { switch (action) { case 'play': // if (action === 'play') console.log('Playing the game...'); startGame({ mode: 'multiplayer' }); break; case 'pause': // else if (action === 'pause') console.log('Pausing the game...'); pauseGame(); break; case 'stop': console.log('Stopping the game...'); endGame(); goToMainMenu(); break; case 'cheat': console.log('Activating cheat mode...'); enableCheatMode(); break; default: // else console.log('Invalid action!'); break; } }

The unique power of switch statements comes from being able to omit the break at the end of each case and let execution “fallthrough” to the next case:

JavaScript
// Generate a random number between 1 and 6 to simulate rolling a dice const diceRoll = Math.floor(Math.random() * 6) + 1; console.log(`You rolled a ${diceRoll}!`); switch (diceRoll) { case 1: console.log('Oops, you rolled a one!'); console.log('You lose a turn.'); break; case 2: console.log( 'Two heads are better than one... sometimes' ); case 4: case 6: // else if (diceRoll === 2 || diceRoll === 4 || diceRoll === 6) console.log('Nice roll!'); console.log('You move forward two spaces.'); break; // ... default: console.log('Invalid dice roll.'); }

Most other languages with switch-case allow this, with the notable exception of C#.

4. Key-value object

Key-value objects let us declaratively map inputs to outputs.

JavaScript
const weatherActivities = { sunny: 'go to the beach', rainy: 'watch a movie at home', cloudy: 'take a walk in the park', snowy: 'build a snowman', }; const weather = 'sunny'; console.log(`Okay, so right now it's ${weather}.`); console.log(`Why don't you ${weatherActivities[weather]}?`);

I found this invaluable when creating screens in React Native apps – each with its own loading, error and success states. Here’s a snippet of a screen in one of our apps:

JavaScript
// screens/course-list.tsx // ... import { ActivityIndicator, Text // ... } from 'react-native-paper'; type ViewState = 'loading' | 'error' | 'data'; export function Courses({ route, navigation }) { const [viewState, setViewState] = useState<ViewState>('loading'); const state = useAppState(); const courses = state?.courseList; // ... const contentDict = { loading: <ActivityIndicator />, error: <Text>Error</Text>, data: <CourseList courses={courses} />, }; const content = contentDict[viewState]; return ( <View> <StatusBar /> {content} </View> ); } // ...

Final thoughts

So if statements aren’t bad at all and are great for writing conditional logic in an easily understandable way. But it’s important to consider alternative approaches to make code shorter, clearer and even more expressive. It’s not about eliminating if statements entirely, but rather adopting effective techniques that make our code more efficient and elegant.

How to HACK JavaScript with Well-Known Symbols (5 ways)

They call them well-known symbols – even though most developers have never used them or even heard of them.

They’re a really cool feature you can use to make magic like this happen:

Use well-known symbols to magically redefine JavaScript's core functionalities to behave in unique and delightful ways.

You’ll see how we built the List class with a well-known symbol to do this.

They’re all about completely customizing the normal behavior of built-in operations like for..of. It’s like operator overloading in C++ and C#.

Also all static methods of the Symbol class.

1. Symbol.hasInstance

So first up we have Symbol.hasInstance: for easily changing how the instanceof operator behaves.

JavaScript
const person = new Person({ name: 'Tari Ibaba', at: 'codingbeautydev.com', }); // Because of Symbol.hasInstance console.log(person instanceof Person); // ❌ false (!!) console.log('Tari Ibaba' instanceof Person); // ✅ true console.log('Person' instanceof Person); // ❌ false

Normally instanceof is all about checking if a variable is an instance of class.

JavaScript
class Person { constructor({ name, at }) { this.name = name; this.at = at; } } const person = new Person({ name: 'Tari Ibaba', at: 'codingbeautydev.com', }); console.log(person instanceof Person); // ✅ true console.log('Person' instanceof Person); // ❌ false console.log(person instanceof String); // ❌ false

This is as it should be. Pretty standard stuff.

But with Symbol.hasInstance we can completely transform how instanceof works:

JavaScript
class Person { constructor({ name, at }) { this.name = name; this.at = at; } static [Symbol.hasInstance](obj) { const people = ['Tari Ibaba', 'Ronaldo']; return people.includes(obj); } }

Now a Person is no longer a Person, as far as instanceof is concerned.

JavaScript
const person = new Person({ name: 'Tari Ibaba', at: 'codingbeautydev.com', }); console.log(person instanceof Person); // ❌ false (!!) console.log('Tari Ibaba' instanceof Person); // ✅ true console.log('Person' instanceof Person); // ❌ false

What if we don’t want to completely override it, but instead extend it in an intuitive way?

We can’t use instanceof inside the symbol because that’ll quickly lead to an infinite recursion:

Instead we compare the special constructor property of the object to our own:

JavaScript
class Fruit { constructor(name) { this.name = name; } [Symbol.hasInstance](obj) { const fruits = ['🍍', '🍌', '🍉', '🍇']; // this == this.constructor in a static method return obj.constructor === this || fruits.includes(obj); } } const fruit = new Fruit('apple');

If you’re just hearing of .constructor, this should explain everything:

JavaScript
String.prototype.constructor.prototype.constructor === String // true

2. Symbol.iterator

Our next hack is Symbol.iterator, for totally altering how and if loop works on an object.

Remember this:

We did this thanks to Symbol.iterator:

JavaScript
class List { elements = []; wordEmojiMap = { red: '🔴', blue: '🔵', green: '🟢', yellow: '🟡', }; add(element) { this.elements.push(element); return this; } // Generator *[Symbol.iterator]() { for (const element of this.elements) { yield this.wordEmojiMap[element] ?? element; } } }

We see generators crop up once again.

Any time we use for..of

JavaScript
const numbers = [1, 2, 3]; for (const num of numbers) { console.log(num); } /* 1 2 3 */

This happens behind the scenes:

JavaScript
const iterator = numbers[Symbol.iterator](); // for..of: Keep calling .next() and using value until done console.log(iterator.next()); // Object {value: 1, done: false} console.log(iterator.next()); // Object {value: 2, done: false} console.log(iterator.next()); // Object {value: 3, done: false} console.log(iterator.next()); // Object {value: undefined, done: true}

So with Symbol.iterator we completely changed what for..of does with any List object:

JavaScript
class List { // ... *[Symbol.iterator]() { for (const element of this.elements) { yield this.wordEmojiMap[element] ?? element; } } }
JavaScript
const colors = new List(); colors.add('red').add('blue').add('yellow'); const iterator = colors[Symbol.iterator](); console.log(iterator.next()); // { value: '🔴', done: false } console.log(iterator.next()); // { value: '🔵', done: false } console.log(iterator.next()); // { value: '🟡', done: false } console.log(iterator.next()); // { value: undefined, done: true }

4. Symbol.toPrimitive

With Symbol.toPrimitive we quickly go from this:

To this:

We did this by overriding Symbol.toPrimitive:

JavaScript
class Person { constructor({ name, at, favColor }) { this.name = name; this.at = at; this.favColor = favColor; } [Symbol.toPrimitive]() { return `I'm ${this.name}`; } }

Now we can use a Person object anywhere we use a string for interpolation & concatenation:

JavaScript
const str = 'Person: ' + person; console.log(str); // Person: I'm Tari Ibaba

There’s even a hint parameter that makes an object act like a number, string, or something else.

JavaScript
class Money { constructor(amount, currency) { this.amount = amount; this.currency = currency; } [Symbol.toPrimitive](hint) { if (hint === 'string') { return `${this.amount} ${this.currency}`; } else if (hint === 'number') { return this.amount; } else if (hint === 'default') { return `${this.amount} ${this.currency}`; } } } const price = new Money(500, 'USD'); console.log(String(price)); // 500 USD console.log(+price); // 500 console.log('Price is ' + price); // Price is 500 USD

4. Symbol.split

Genius well-known symbol for turning your custom objects into string separators:

JavaScript
class Greedy { [Symbol.split](str) { return `Me: ${str}, you: 0`; } } class ReadableNumber { [Symbol.split](str) { return str.split('').reduceRight((acc, cur, index, arr) => { return index % 3 === 0 && index < arr.length - 1 ? cur + ',' + acc : cur + acc; }, ''); } } console.log('1-000-000'.split('-')); // [ '1', '000', '000' ] console.log('1000000'.split(new Greedy())); // Me: 1000000, you: 0 console.log('1000000'.split(new ReadableNumber())); // 1,000,000

5. Symbol.search

Just like Symbol.split, transform your custom objects into sophisticated string searching tools:

JavaScript
class Topic { static topics = { 'codingbeautydev.com': ['JavaScript', 'VS Code', 'AI'], }; constructor(value) { this.value = value; } [Symbol.search](where) { const topic = this.constructor.topics[where]; if (!topic) return -1; return topic.indexOf(this.value); } } const str = 'codingbeautydev.com'; console.log(str.search(new Topic('VS Code'))); // 1 console.log(str.search(new Topic('Economics'))); // -1

Final thoughts

From looping to splitting to searching, well-known symbols let us redefine our core functionalities to behave in unique and delightful ways, pushing the boundaries of what’s possibly in JavaScript.

10 amazing web development tools you need to know (#2)

10 more amazing web dev tools to boost your workflow and make development more enjoyable.

From stunning animations to rapid API creation & documentation, these tools will help you get things done faster than ever.

1. Heat.js

Create stunning heat maps and charts with this incredible UI library.

Like this:

GitHub-like heatmap.

Very similar to my GitHub profile (but with way more greens of course):

No external libraries needed, it’s dependency-free.

Customize every detail with a wide range of settings, or choose from over 10 pre-made themes in 40+ languages. Get ready to bring your data to life.

2. Postman

Simply the best for creating and testing APIs.

If you still use curl then you must living in the stone age. Or maybe you have some sort of CLI superiority complex.

Just making a simple POST request is pain; Stressful editing, strict formatting requirements that don’t even stay consistent with the OS and terminal.

1st one works on Linux, 2nd on Windows CMD (I guess), 3rd on Powershell…😴

Why go through any of that when you have a nice and easy GUI with none of these problems?

Body data is easy, query params are easy.

It even has built-in support for testing APIs from Paypal and Google.

3. React Toastify

By far the easiest way I found to add toast notifications to your React app.

All you need is this simple code:

JavaScript
import React from 'react'; import { ToastContainer, toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; function App(){ const notify = () => toast("Wow so easy!"); return ( <div> <button onClick={notify}>Notify!</button> <ToastContainer /> </div> ); }

Look it even has all sorts of colorful progress bars to show how much time left before it goes away 👇

And dark mode of course. And different themes for different purposes.

4. GitLens for VS Code

VS Code source control on steroids.

Packed full with tons of views with essential repo data and current file info: file history, commits, branches, remotes, and more.

Even history of particular line in the file, with valuable related data and actions.

5. Palettify

Craft stunning UI color schemes in real-time with Palettify.

That H1 font is awesome by the way.

Play around with shadcn-ui components, toggle light/dark mode, then grab the the CSS theme code with a single click.

Several themes to choose from:

Wiza theme:

6. LinkPreview

I use this to easily get a preview image from a URL.

Like Notion does for their Bookmark block:

Better than depending on unreliable web scraping libraries, or rigid 3rd-party Link Preview UI components.

You just make an API request to this URL:

Plain text
https://api.linkpreview.net/?q=your_url_to_preview

And you easily get your image, along with relevant information for preview.

If we go to the image URL we’ll see it’s the same exact one Notion showed:

7. Lottie

Breathe life into your apps with Lottie.

Lottie takes the magic from Adobe After Effects and brings it straight to your mobile and web apps.

Imagine the experience needed to create this 👇 Then imagine recreating it with raw CSS.

So no more hand-coding – Lottie uses a special plugin to turn those After Effects animations into a lightweight JSON format that works flawlessly on any device.

8. Free Icons

Unleash your creativity with a treasure trove of over 22,000 icons!

Dive into a world of beautiful and free SVG icons, all meticulously tagged and ready to be discovered at your fingertips.

Including dinosaurs like Internet Explorer.

Simply type in a keyword and browse through countless icons to find the perfect visual match for your project.

9. jwt.io

I use this often when having nasty bugs with JSON web token from Firebase Auth or some stuff.

You just paste your token on the left and you instantly see the decoded value on the right.

And using this got me confused at first; I thought JWT was like an encryption where only the receiver could know what was there with the secret?

But no; it turned out JWT is for verifying the source of the message, not hiding information. Like in crypto wallets and transactions private and public keys.

10. TypeSpec

Use this to stop wasting time manually documenting your API for public use.

Describe your data once, and TypeScript magically conjures up everything you need: schema, API specifications, client / server code, docs, and more.

Final thoughts

Use these awesome tools to upgrade your productivity and make development more fun.

Stop using nested ifs: Do this instead

Typical use case for nested ifs: you want to perform all sorts of checks on some data to make sure it’s valid before finally doing something useful with it.

Don’t do this! 👇

JavaScript
function sendMoney(account, amount) { if (account.balance > amount) { if (amount > 0) { if (account.sender === 'user-token') { account.balance -= amount; console.log('Transfer completed'); } else { console.log('Forbidden user'); } } else { console.log('Invalid transfer amount'); } } else { console.log('Insufficient funds'); } }

There’s a better way:

JavaScript
function sendMoney(account, amount) { if (account.balance < amount) { console.log('Insufficient funds'); return; } if (amount <= 0) { console.log('Invalid transfer amount'); return; } if (account.sender !== 'user-token') { console.log('Forbidden user'); return; } account.balance -= amount; console.log('Transfer completed'); }

See how much cleaner it is? Instead of nesting ifs, we have multiple if statements that do a check and return immediately if the condition wasn’t met. In this pattern, we can call each of the if statements a guard clause.

If you do a lot of Node.js, you’ve probably seen this flow in Express middleware:

JavaScript
function authMiddleware(req, res, next) { const authToken = req.headers.authorization; if (!authToken) { return res.status(401).json({ error: 'Unauthorized' }); } if (authToken !== 'secret-token') { return res.status(401).json({ error: 'Invalid token' }); } if (req.query.admin === 'true') { req.isAdmin = true; } next(); }

It’s much better than this, right? :

JavaScript
function authMiddleware(req, res, next) => { const authToken = req.headers.authorization; if (authToken) { if (authToken === 'secret-token') { if (req.query.admin === 'true') { req.isAdmin = true; } return next(); } else { return res.status(401).json({ error: 'Invalid token' }); } } else { return res.status(401).json({ error: 'Unauthorized' }); } };

You never go beyond one level of nesting. We can avoid the mess that we see in callback hell.

How to convert nested ifs to guard clauses

The logic for this for doing this is simple:

1. Find the innermost/success if

Here we can clearly see it’s the cond3 if. After this, if we don’t do any more checks and take the action we’ve always wanted to take.

JavaScript
function func(cond1, cond2, cond3) { if (cond1) { if (cond2) { if (cond3) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 3'); } } else { console.log('failed condition 2'); } } else { console.log('failed condition 1'); } }

2. Invert the outermost if and return

Negate the if condition to put the else statements’ body in there and add a return after.

Delete the else braces (keep the body, it still contains the formerly nested ifs, and move the closing if brace to just after the return.

So:

JavaScript
function func(cond1, cond2, cond3) { if (!cond1) { // 👈 inverted if condition // 👇 body of former else clause console.log('failed condition 1'); return; // 👈 exit on fail } // 👇 remaining nested ifs to convert to guard clauses if (cond2) { if (cond3) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 3'); } } else { console.log('failed condition 2'); } }

3. Do the same for each nested if until you reach the success if

And then:

JavaScript
function func(cond1, cond2, cond3) { if (!cond1) { console.log('failed condition 1'); return; } if (!cond2) { console.log('failed condition 2'); return; } // 👇 remaining nested ifs to convert if (cond3) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 3'); } }

And finally:

JavaScript
function func(cond1, cond2, cond3) { if (!cond1) { console.log('failed condition 1'); return; } if (!cond2) { console.log('failed condition 2'); return; } if (!cond3) { console.log('failed condition 3'); return; } console.log('PASSED!'); console.log('taking success action...'); }

I use the JavaScript Booster extension to make inverting if statements in VS Code much easier.

Here we only had to put the cursor in the if keyword and activate the Show Code Actions command (Ctrl + . by default).

Check out this article for an awesome list of VSCode extensions you should definitely install alongside with JavaScript Booster.

Tip: Split guard clauses into multiple functions and always avoid if/else

What if we want to do something other after checking the data in an if/else? For instance:

JavaScript
function func(cond1, cond2) { if (cond1) { if (cond2) { console.log('PASSED!'); console.log('taking success action...'); } else { console.log('failed condition 2'); } console.log('after cond2 check'); } else { console.log('failed condition 1'); } console.log('after cond1 check'); }

In this function regardless of cond1‘s value, the 'after cond1 check' the line will still print. Similar thing for the cond2 value if cond1 is true.

In this case, it takes a bit more work to use guard clauses:

If we try to use guard clauses, we’ll end up repeating the lines that come after the if/else checks:

JavaScript
function func(cond1, cond2) { if (!cond1) { console.log('failed condition 1'); console.log('after cond1 check'); return; } if (!cond2) { console.log('failed condition 2'); console.log('after cond2 check'); console.log('after cond1 check'); return; } console.log('PASSED!'); console.log('taking success action...'); console.log('after cond2 check'); console.log('after cond1 check'); } func(true);

Because the lines must be printed, we print them in the guard clause before returning. And then, we print it in all(!) the following guard clauses. And once again, in the main function body if all the guard clauses were passed.

So what can we do about this? How can we use guard clauses and still stick to the DRY principle?

Well, we split the logic into multiple functions:

JavaScript
function func(cond1, cond2) { checkCond1(cond1, cond2); console.log('after cond1 check'); } function checkCond1(cond1, cond2) { if (!cond1) { console.log('failed condition 1'); return; } checkCond2(cond2); console.log('after cond2 check'); } function checkCond2(cond2) { if (!cond2) { console.log('failed condition 2'); return; } console.log('PASSED!'); console.log('taking success action...'); }

Let’s apply this to the Express middleware we saw earlier:

JavaScript
function authMiddleware(req, res, next) { checkAuthValidTokenAdmin(req, res, next); } function checkAuthValidTokenAdmin(req, res, next) { const authToken = req.headers.authorization; if (!authToken) { return res.status(401).json({ error: 'Unauthorized' }); } checkValidTokenAdmin(req, res, next); } function checkValidTokenAdmin(req, res, next) { const authToken = req.headers.authorization; if (authToken !== 'secret-token') { return res.status(401).json({ error: 'Invalid token' }); } checkAdmin(req, res, next); } function checkAdmin(req, res, next) { if (req.query.admin === 'true') { req.isAdmin = true; } next(); }

In a way, we’ve replaced the if/else statements with a chain of responsibility pattern. Of course, this might be an overkill for simple logic like a basic Express request middleware, but the advantage here is that it delegates each additional check to a separate function, separating responsibilities and preventing excess nesting.

Key takeaways

Using nested ifs in code often leads to complex and hard-to-maintain code; Instead, we can use guard clauses to make our code more readable and linear.

We can apply guard clauses to different scenarios and split them into multiple functions to avoid repetition and split responsibilities. By adopting this pattern, we end up writing cleaner and more maintainable code.

The 5 most transformative JavaScript features from ES14

JavaScript has come a long way in the past 10 years with brand new feature upgrades in each one.

Still remember when we created classes like this?

JavaScript
function Car(make, model) { this.make = make; this.model = model; } // And had to join strings like this Car.prototype.drive = function() { console.log("Vroom! This " + this.make + " " + this.model + " is driving!"); };

Yeah, a lot has changed!

Let’s take a look at the 5 most significant features that arrived in ES14 (2023); and see the ones you missed.

1. toSorted()

Sweet syntactic sugar.

ES14’s toSorted() method made it easier to sort an array and return a copy without mutation.

Instead of this:

JavaScript
const nums = [5, 2, 6, 3, 1, 7, 4]; const sorted = clone.sort(); console.log(sorted); // [1, 2, 3, 4, 5, 6, 7] // ❌❌ Mutated console.log(nums); // [1, 2, 3, 4, 5, 6, 7]

We now got to do this ✅:

JavaScript
const nums = [5, 2, 6, 3, 1, 7, 4]; // ✅ toSorted() prevents mutation const sorted = nums.toSorted(); console.log(sorted); // [1, 2, 3, 4, 5, 6, 7] console.log(nums); // [5, 2, 6, 3, 1, 7, 4]

toSorted() takes a callback for controlling sorting behavior – ascending or descending, alphabetical or numeric. Just like sort().

2. Array find from last

Searching from the first item isn’t always ideal:

JavaScript
const tasks = [ { date: '2017-03-05', name: '👟run a 5k' }, { date: '2017-03-04', name: '🏋️lift 100kg' }, { date: '2017-03-04', name: '🎶write a song' }, // 10 million records... { date: '2024-04-24', name: '🛏️finally sleep on time' }, { date: '2024-04-24', name: '📝1h writing with no breaks' }, ]; const found = tasks.find((item) => item.date === '2024-03-25'); const foundIndex = tasks.findIndex((item) => item.date === '2024-03-25'); console.log(found); // { value: '2024-03-25', name: 'do 1000 pushups' } console.log(foundIndex); // 9,874,910

You can easily see that it’ll be much faster for me to search our gigantic list from the end instead of start.

JavaScript
const tasks = [ { date: '2017-03-05', name: 'run a 5k' }, { date: '2017-03-04', name: 'lift 100kg' }, { date: '2017-03-04', name: 'write a song' }, // 10 million records... { date: '2024-04-24', name: 'finally sleep on time' }, { date: '2024-04-24', name: '1h writing with no breaks' }, ]; // ✅ Much faster const found = tasks.findLast((item) => item.date === '2024-03-25'); const foundIndex = tasks.findLastIndex((item) => item.date === '2024-03-25'); console.log(found); // { value: '2024-03-25', name: 'do 1000 pushups' } console.log(foundIndex); // 9,874,910

And they’re also times you MUST search from the end for your program work.

Like we want to find the last even number in a list of numbers, find and findIndex will be incredibly off.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // ❌ gives 14, instead of 10 const lastEven = nums.find((value) => value % 2 === 0); // ❌ gives 1, instead of 4 const lastEvenIndex = nums.findIndex((value) => value % 2 === 0); console.log(lastEven); // 14 console.log(lastEvenIndex); // 1

And calling reverse() won’t work either, even as slow as it would be:

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // ❌ Copying the entire array with the spread syntax before // calling reverse() const reversed = [...nums].reverse(); // correctly gives 10 const lastEven = reversed.find((value) => value % 2 === 0); // ❌ gives 1, instead of 4 const reversedIndex = reversed.findIndex((value) => value % 2 === 0); // Need to re-calculate to get original index const lastEvenIndex = reversed.length - 1 - reversedIndex; console.log(lastEven); // 10 console.log(reversedIndex); // 1 console.log(lastEvenIndex); // 4

So in cases like where the findLast() and findLastIndex() methods come in handy.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // ✅ Search from end const lastEven = nums.findLast((num) => num % 2 === 0); // ✅ Maintain proper indexes const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0); console.log(lastEven); // 10 console.log(lastEvenIndex); // 4

This code is shorter and more readable. Most importantly, it produces the correct result.

3. toReversed()

Another new Array method to promote immutability and functional programming.

Before – with reverse() ❌:

JavaScript
const arr = [5, 4, 3, 2, 1]; const reversed = arr.reverse(); console.log(reversed); // [1, 2, 3, 4, 5] // ❌ Original modified console.log(arr); // [1, 2, 3, 4, 5]

Now – with toReversed() ✅:

JavaScript
const arr = [5, 4, 3, 2, 1]; const reversed = arr.toReversed(); console.log(reversed); // [1, 2, 3, 4, 5] // ✅ No modification console.log(arr); // [5, 4, 3, 2, 1]

I find these immutable methods awesome for constantly chaining methods over and over without worrying about the original variables:

JavaScript
// ✅ Results are independent of each other const nums = [5, 2, 6, 3, 1, 7, 4]; const result = nums .toSorted() .toReversed() .map((n) => n * 2) .join(); console.log(result); // 14,12,10,8,6,4,2 const result2 = nums .map((n) => 1 / n) .toSorted() .map((n) => n.toFixed(2)) .toReversed(); console.log(result2); // [ '1.00', '0.50', '0.33', '0.25', // '0.20', '0.17', '0.14' ]

4. toSpliced()

Lovers of functional programming will no doubt be pleased with all these new Array methods.

This is the immutable counterpart of .splice():

JavaScript
const colors = ['red🔴', 'purple🟣', 'orange🟠', 'yellow🟡']; // Remove 2 items from index 1 and replace with 2 new items const spliced = colors.toSpliced(1, 2, 'blue🔵', 'green🟢'); console.log(spliced); // [ 'red🔴', 'blue🔵', 'green🟢', 'yellow🟡' ] // Original not modified console.log(colors); // ['red🔴', 'purple🟣', 'orange🟠', 'yellow🟡'];

5. Array with() method

with() is our way of quickly change an array element with no mutation whatsoever.

Instead of this usual way:

JavaScript
const arr = [5, 4, 7, 2, 1] // Mutates array to change element arr[2] = 3; console.log(arr); // [5, 4, 3, 2, 1]

ES14 now let us do this:

JavaScript
const arr = [5, 4, 7, 2, 1]; const replaced = arr.with(2, 3); console.log(replaced); // [5, 4, 3, 2, 1] // Original not modified console.log(arr); // [5, 4, 7, 2, 1]

Final thoughts

They were other features but ES14 was all about easier functional programming and built-in immutability.

With the rise of React we’ve seen declarative JavaScript explode in popularity; it’s only natural that more of them come baked into the language as sweet syntactic sugar.

Shuffling algorithm in 1 line instead of 10: functional Fisher-Yates

Can we write the Fisher-Yates shuffling algorithm in a declarative and functional way with zero mutations, all in a single one-liner statement?!

Let’s find out.

The normal way of writing Fisher-Yates

Some time ago I was creating an indie card game for Android and I needed to shuffle the cards before sharing them to the user and opponents.

Well I didn’t know about the standard shuffling algorithms, so after some thought I came up with this:

But after reading more about Fisher-Yates on Wikipedia, I discovered serious problems with my algorithm:

JavaScript
// It was in C#, but like this function shuffleArray(array) { const clone = [...array]; for (let i = clone.length - 1; i > 0; i--) { // Swap i with random item from 0..n const randomIndex = Math.floor( Math.random() * clone.length // ❌❌ ); const temp = clone[i]; clone[i] = clone[randomIndex]; clone[randomIndex] = temp; } return clone; } console.log(shuffleArray(['C', 'I', 'O', 'L', 'G'])); // [ 'I', 'L', 'O', 'G', 'C' ] console.log(shuffleArray(['C', 'I', 'O', 'L', 'G'])); // [ 'L', 'O', 'G', 'I', 'C' ]

I was swapping each item with a random element in the range of 0..n, but this was wrong.

As explained here, this made it more likely for the array to get shuffled in particular ways.

The right way was to use the range 0..i to swap with the current element i in the loop.

JavaScript
function shuffleArray(array) { const clone = [...array]; for (let i = clone.length - 1; i > 0; i--) { const randomIndex = Math.floor(Math.random() * (i + 1)); const item = clone[i]; clone[i] = clone[randomIndex]; clone[randomIndex] = item; } return clone; } console.log(shuffleArray(['C', 'I', 'O', 'L', 'G'])); // [ 'G', 'L', 'I', 'C', 'O' ] console.log(shuffleArray(['C', 'I', 'O', 'L', 'G'])); // [ 'L', 'O', 'G', 'I', 'C' ]

With this I could make sure every possible shuffled result would have an equal chance of happening.

Functional, immutable, one-liner Fisher-Yates

How exactly are we supposed to go about this?

Can we try this?👇

JavaScript
const shuffleArray = (arr) => arr.reduce((array, item, i) => { const randomIndex = Math.floor( Math.random() * arr.length ); [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]; return arr; });

No we can’t; We used ES6 swapping to shorten the code but we still mutated the array. And cloning doesn’t count.

We need to figure out a way to swap the array elements immutably – create a new array with items swapped.

Using conditionals we can easily come up with this:

JavaScript
const immutableSwap = (arr, i, j) => arr.map((item, index) => index === i ? arr[j] : index === j ? arr[i] : item ); const arr = ['B', 'E', 'A', 'U', 'T', 'Y']; console.log(immutableSwap(arr, 2, 4)); // [ 'B', 'E', 'T', 'U', 'A', 'Y' ]

But we could also use Object.values like this:

JavaScript
const immutableSwap = (arr, i, j) => { return Object.values({ ...arr, [i]: arr[j], [j]: arr[i], }); }; console.log(immutableSwap(arr, 3, 5)); // [ 'B', 'Y', 'A', 'U', 'T', 'E' ]

What’s next?

With the immutable swap worked out, our game plan is pretty straightforward:

  1. For each item i in 0..n, immutably select an index in 0..i to swap with i for each i in 0..n
  2. Loop through the array again and immutably swap each element with its assigned swapping pair.

For #1 we can easily use map() to create an array with the new random positions for every index:

JavaScript
const getShuffledPosition = (arr) => { return [...Array(arr.length)].map((_, i) => Math.floor(Math.random() * (i + 1)) ); }; /* [0, 2, 0, 1] swap item at: 1. index 0 with index 0 2. index 1 with index 2 3. index 2 with index 0 4. index 3 with index 1 */ getShuffledPosition(['🔵', '🔴', '🟡', '🟢']); getShuffledPosition(['🔵', '🔴', '🟡', '🟢']); // [0, 1, 1, 3] getShuffledPosition(['🔵', '🔴', '🟡', '🟢']); // [0, 0, 2, 1]

What about #2?

We’re outputting an array, but we can’t use map again because the transformation at each step depends on the full array and not just the current element.

So we’ll use reduce():

JavaScript
const shuffleUsingPositions = (arr) => getShuffledPosition(arr).reduce( (toShuffle, newPosition, originalIndex) => immutableSwap(toShuffle, originalIndex, newPosition), arr ); shuffleUsingPositions(['🔵', '🔴', '🟡', '🟢']) // [ '🔴', '🔵', '🟢', '🟡' ]

Not let’s de-abstract the immutable functions so we have a true and complete one-liner:

JavaScript
const shuffleArray = (arr) => [...Array(arr.length)] .map((_, i) => Math.floor(Math.random() * (i + 1))) .reduce( (toShuffle, newPosition, originalIndex) => toShuffle.map((item, index) => index === originalIndex ? toShuffle[newPosition] : item === newPosition ? originalIndex : item ), arr );

Or – faster and more readable:

JavaScript
const shuffleArray = (arr) => [...Array(arr.length)] .map((_, i) => Math.floor(Math.random() * (i + 1))) .reduce( (toShuffle, newPosition, originalIndex) => Object.values({ ...toShuffle, [originalIndex]: toShuffle[newPosition], [newPosition]: toShuffle[originalIndex], }), arr );
JavaScript
shuffleArray(['🔵', '🔴', '🟡', '🟢']) // (3x) // [ '🟡', '🔴', '🔵', '🟢' ] // [ '🔴', '🟡', '🟢', '🔵' ] // [ '🟡', '🔵', '🔴', '🟢' ]

Just like you can always write a recursive algorithm as a loop, you can write every imperative iteration as a brilliant single-statement chain of array methods.

They all work perfectly.

10 powerful tools for faster web development

10 fantastic web dev tools to level up your productivity and achieve your coding goals faster than ever.

From colorful styling effects to faster typing, these tools will boost your workflow and make a lot of things easier.

1. React Glow

Create a playful glow that dances with the user’s mouse, adding a touch of magic to your interface:

Whenever I see effects like this on a page, it just gives me an aura of sophistication and quality.

2. Vite

Ditch Create React App and make development a breeze with Vite.

Say hello to lightning-fast hot module replacement and built-in TypeScript support.

And absolutely no need to worry about outdated dependencies or vulnerable packages – unlike with Create React App:

On the other hand, this is what I get for using dinosaur Create React App in one my Electron projects:

3. TODO Highlight for VS Code

With TODO highlight I can quickly add TODO comments anywhere in my codebase and keep track of all them effortlessly.

You quickly add a TODO with comment prefixed with TODO:

Then view in the Output window after running the TODO-Highlight: List highlighted annotations command:

I find it a great alternative to storing them in a to-do list app, especially when they’re something very low-level and contextual, for example: “TODO: Use 2 map()’s instead of reduce()”.

4. Rough Notation

Powerful JS library for creating and animating colorful annotations on a web page with a hand-drawn look and feel.

When I see this I see a human touch in the deliberate imperfections; it stands out.

So there’s underline, box, circle, highlight, strike-through… many many annotation styles to choose from with duration and color customization options.

5. JavaScript (ES6) Code Snippets for VS Cod

VS Code extension fully loaded with heaps of time-saving JavaScript code snippets for ES6.

Snippets like imp and imd:

A demo of how to use the JavaScript (ES6) Code Snippets extension.

6. background-removal-js

This free, browser-based JavaScript library lets you easily remove backgrounds from your images all while keeping your data private.

7. VanJS

This is a super small and simple library for building user interfaces.

It uses plain JavaScript and the built-in DOM functionality, just like React. But unlike React it doesn’t need any special syntax for defining UI elements.

JavaScript
// Reusable components can be just pure vanilla JavaScript functions. // Here we capitalize the first letter to follow React conventions. const Hello = () => div( p("👋Hello"), ul( li("🗺️World"), li(a({href: "https://codingbeautydev.com/"}, "💻Coding Beauty")), ), ) van.add(document.body, Hello()) // Alternatively, you can write: // document.body.appendChild(Hello())

8. Mailo

This drag-and-drop builder makes crafting beautiful, responsive emails a breeze. No more coding headaches, just watch your emails light up inboxes across all devices.

9. Color Names

Dive into a treasure trove of over 30,000 color names, meticulously gathered from across the web and lovingly enriched by thousands of passionate users.

Find the perfect shade to ignite your creativity and bring your designs to life

10. Flowbite Icons

Unleash a treasure trove of 450+ stunning SVG icons, all open-source and ready to bring your web projects to life.

Whether you prefer bold solids or crisp outlines, these icons seamlessly integrate with Flowbite and Tailwind CSS, making customization a breeze.

Final thoughts

Use these awesome tools to level up your productivity and developer quality of life.

VS Code: 5 rapid file creation tips for greater productivity

From painfully slow to lightning-fast, let’s look at all the 5 ways to create a file in VS Code.

And fastest way adds new files without having to use your mouse at all! We’ll see…

5. File > New File…

I’m pretty sure very few people use this apart from those who are new to text editors.

You move your mouse all the way up to File then click New File…

Then you’ve still got to enter the filename:

THEN, a file picker dialog for you to choose the folder – never mind VS Code having its own built-in file manager.

Before finally:

Create: New File

This is almost like the first, except you use the Create: New File from the Command Palette.

4. Double-click tab bar

Not many know about this method… double-clicking the file tab bar:

Ctrl + N

Or use the faster Ctrl + N keyboard shortcut.

So after Ctrl + N you either manually select a language:

Or you just start typing and wait for language auto-detection:

It’s useful when you don’t have any open project and you just want a quick file to work on.

You’re still got to save it though:

3. New File… icon button

This is one of the more popular ways; clicking the New File... icon button in the File Explorer Pane:

2. Double-click file explorer pane

This works great for top-level files.

1. A

Opening keyboard shortcuts like this:

And editing it like this:

To create files faster than ever at the single press of a key:

They all have different speeds but they’re all useful. VS Code’s vast versatility is unmatched.

10 amazing one-liners to transform your JavaScript code

Cool one-liner solutions to interesting JavaScript problems.

1. Baffling shuffling magic: Arrays

Everyone always talks about shuffling arrays with sort() and Math.random() - 0.5

But have you met my friend? 👇

JavaScript
const shuffleArray = (arr) => [...Array(arr.length)] .map((_, i) => Math.floor(Math.random() * (i + 1))) .reduce( (shuffled, r, i) => shuffled.map((num, j) => j === i ? shuffled[r] : j === r ? shuffled[i] : num ), arr ); // [ 2, 4, 1, 3, 5 ] (varies) console.log(shuffleArray([1, 2, 3, 4, 5]));

And isn’t it cool how the data just flows freely from input to output? No stops for intermediary values along the way👍

2. A need for belonging: Group array by ID

JavaScript
const groupBy = (arr, groupFn) => arr.reduce( (grouped, obj) => ({ ...grouped, [groupFn(obj)]: [ ...(grouped[groupFn(obj)] || []), obj, ], }), {} ); const fruits = [ { name: 'pineapple🍍', color: '🟡' }, { name: 'apple🍎', color: '🔴' }, { name: 'banana🍌', color: '🟡' }, { name: 'strawberry🍓', color: '🔴' }, ]; const groupedByNameLength = groupBy( fruits, (fruit) => fruit.color ); console.log(groupedByNameLength);

3. An ID for everyone

Perfection.

JavaScript
const generateRandomUUID = (a) => a ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace( /[018]/g, generateRandomUUID ); console.log(generateRandomUUID()); // f138f635-acbd-4f78-9be5-ca3198c4cf34 console.log(generateRandomUUID()); // 8935bb0d-6503-441f-bb25-7bc685b5b5bc

Just look at the language concepts working together here; there’s basic arithmetic, powers, random values, methods, bitshifting, regexes, callback functions, recursion, exponentiation… it’s perfection.

4. Flipping the string script: Reverse a string

Not this✖️

JavaScript
const reverseString = (str) => { let reversed = ''; for (let i = str.length - 1; i >= 0; i--) { const ch = str[i]; reversed += ch; } return reversed; }; const reverse = reverseString('Indestructible!'); console.log(reverse); // !elbitcurtsednI

But this✅:

JavaScript
const reverseString = (str) => str.split('').reverse().join(''); const reverse = reverseString('Indestructible!'); console.log(reverse); // !elbitcurtsednI

5. Painting with code: Generate random hex color

1 line to generate a random hex color:

JavaScript
const randomHexColor = () => `#${Math.random().toString(16).slice(2, 8).padEnd(6, '0')}`; console.log(randomHexColor()); // #7a10ba (varies) console.log(randomHexColor()); // #65abdc (varies)

6. Array equality

Check array equality with a one-liner…

Not this✖️:

JavaScript
const areEqual = (arr1, arr2) => { if (arr1.length === arr2.length) { for (const num of arr1) { if (!arr2.includes(num)) { return false; } } return true; } return false; };

But this✅:

JavaScript
const areEqual = (arr1, arr2) => arr1.sort().join(',') === arr2.sort().join(',');

Or✅:

JavaScript
// For more complex objects // and sort() will probably have a defined callback const areEqual = (arr1, arr2) => JSON.stringify(arr1.sort()) === JSON.stringify(arr2.sort());

7. Clean and clutter-free arrays

Shortest way to remove duplicates from an array?

Not this✖️:

JavaScript
const removeDuplicates = (arr) => { const result = []; for (const num of arr) { if (!result.includes(num)) { result.push(num); } } return result; }; const arr = [1, 2, 3, 4, 5, 3, 1, 2, 5]; const distinct = removeDuplicates(arr); console.log(distinct); // [1, 2, 3, 4, 5]

But this✅:

JavaScript
const arr = [1, 2, 3, 4, 5, 3, 1, 2, 5]; const distinct = [...new Set(arr)]; console.log(distinct); // [1, 2, 3, 4, 5]

These used to be like the only reason anyone ever cared for Sets — until we got these 7 amazing new methods.

8. That’s not an a email: Validation

Email validation one-liners are all about that regex:

JavaScript
const isValidEmail = (email) => /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/.test( email ); console.log(isValidEmail('[email protected]')); // true console.log(isValidEmail('[email protected]')); // false console.log(isValidEmail('codingbeautydev.com')); // false console.log(isValidEmail('hi@')); // false console.log(isValidEmail('hi@codingbeautydev&12')); // false

But I’ve seen monstrosities like this! 👇

JavaScript
const isValidEmail = (email) => /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( email ); console.log(isValidEmail('[email protected]')); // true console.log(isValidEmail('[email protected]')); // false console.log(isValidEmail('codingbeautydev.com')); // false console.log(isValidEmail('hi@')); // false console.log(isValidEmail('hi@codingbeautydev&12')); // false

And even these 😮:

JavaScript
// ❗❗ const isValidEmail = (email) => /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test( email ); console.log(isValidEmail('[email protected]')); // true console.log(isValidEmail('[email protected]')); // false console.log(isValidEmail('codingbeautydev.com')); // false console.log(isValidEmail('hi@')); // false console.log(isValidEmail('hi@codingbeautydev&12')); // false

It’s probably about being as thorough as possible – let’s say the 1st one catches like 95% of wrong emails, then the 2nd like 99%.

The last one here is actually the official RFC 2822 standard for validating emails – so we’re looking at 100% coverage.

9. Data translation: JSON to Maps

JavaScript
const jsonToMap = (json) => new Map(Object.entries(JSON.parse(json))); const json = '{"user1":"John","user2":"Kate","user3":"Peter"}'; const map = jsonToMap(json); // Kate console.log(map.get('user2')); // Map(3) { 'user1' => 'John', 'user2' => 'Kate', 'user3' => 'Peter' } console.log(map);

10. Slithering snake to calm camel

Easily convert from snake casing to camel casing with zero temporary variables.

JavaScript
const snakeToCamelCase = (s) => s .toLowerCase() .replace(/(_\w)/g, (w) => w.toUpperCase().substr(1)); const str1 = 'passion_growth'; const str2 = 'coding_beauty'; console.log(snakeToCamelCase(str1)); // passionGrowth console.log(snakeToCamelCase(str2)); // codingBeauty

Final thoughts

Countless operations jam-packed into a single statement; A race from input start to output finish with no breaks, a free flow of high-level processing. A testament of coding ability and mastery.

This is the power and beauty of JavaScript one-liners.

17 lines of JS code became 1 line after this simple trick

How did these 17 lines of JavaScript code turn into a one-liner function?

It’s easy, you’ll see!

The 1st thing we notice is we’re selecting items from an array – this should NEVER take 3 lines in JavaScript!

We have the destructuring operator to turn this:

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = []; for (const line of lines) { const sourceDestination = line.split(' '); const source = sourceDestination[2]; const destination = sourceDestination[3]; const redirectObj = { source: source, destination: destination, permanent: true, }; sourceDestinationList.push(redirectObj); } return sourceDestinationList; }

into this:

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = []; for (const line of lines) { // ✅ Skip 1st two with comma placeholders! const [, , source, destination] = line.split(' '); const redirectObj = { source: source, destination: destination, permanent: true, }; sourceDestinationList.push(redirectObj); } return sourceDestinationList; }

What else is obvious? A loop and a gradual accumulation (with push).

Which means we’re easily cutting out statements here with map() or reduce():

reduce works well anywhere but map is the natural choice since 1 array clearly transforms to another here.

So we go from this:

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = []; for (const line of lines) { const [, , source, destination] = line.split(' '); const redirectObj = { source: source, destination: destination, permanent: true, }; sourceDestinationList.push(redirectObj); } return sourceDestinationList; }

To this:

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = lines.map((line) => { // ✅ const [, , source, destination] = line.split(' '); const redirectObj = { source: source, destination: destination, permanent: true, }; // ✅ return redirectObj; }); return sourceDestinationList; }

The declaration, for loop and push() have all been eradicated.

Now removing the temporary variable:

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = lines.map((line) => { const [, , source, destination] = line.split(' '); return { source: source, destination: destination, permanent: true, }; }); return sourceDestinationList; }

And combining the last 2 statements in map‘s callback – without calling split() twice.

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = lines.map((line) => { return line.split(' ').reduce( (acc, curr, i) => { return i === 2 ? { ...acc, source: curr } : i === 3 ? { ...acc, destination: curr } : acc; }, { permanent: true } ); }); return sourceDestinationList; }

reduce() has a way of making your head spin!

Let’s go with this instead; much more readable:

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = lines .map((line) => { return line.split(' '); }) .map(([, , source, destination]) => { return { source: source, destination: destination, permanent: true, }; }); return sourceDestinationList; }

Now we can remove the callback braces, from (a) => { return { b; } } to (a) => ({ b }):

JavaScript
function extractRedirects(str) { const lines = str.split('\n'); const sourceDestinationList = lines .map((line) => line.split(' ')) .map(([, , source, destination]) => ({ source: source, destination: destination, permanent: true, })); return sourceDestinationList; }

All that’s left now is to remove the 2 remaining temp vars and use implicit property values:

JavaScript
function extractRedirects(str) { return str .split('\n') .map((line) => line.split(' ')) .map(([, , source, destination]) => ({ source, // ✅ destination, // ✅ permanent: true, })); }

Arrow function:

JavaScript
const extractRedirects = (str) => str .split('\n') .map((line) => line.split(' ')) .map(([, , source, destination]) => ({ source, destination, permanent: true, }));

Yes refactors are fun; it’s fun to see the function gradually evolve and grow in a compact one-liner form.

And it wasn’t just for fancy; With it I moved a GIGANTIC amount of redirect information…

From painful, dinosaur WP Apache .htaccess:

They were way more than 5!

To lovely, modern next.config.js

And it worked perfectly.