Started a collection of elegant syntactical Javascript tricks that — fair warning — may not always be the most performant way to do what we're doing.
Joining an array of optional values
You have this object, and a bunch of properties that it might have...but how to gather them into a coherent list?
Traditionally, you might loop over a list of possible properties, checking for each one and accumulating them into a list manually:
const options = ['first', 'middle', 'last'];
const names = { first: 'John', last: 'Doe' };
let displayName = [];
for (const prop of options) {
if (names.hasOwn(prop)) {
displayName.push(names[prop]);
}
}
displayName = displayName.join(' ');
This works, but it involves maintaining that list of properties, looping through said list, and doing some manual clean up. A more elegant way is to use optional chaining, and Array.filter to discard any falsey values:
const names = { first: 'John', last: 'Doe' };
const displayName = [ names?.first, names?.middle, names?.last ]
.filter(Boolean)
.join(' ')
;
// -> 'John Doe'
Wrapping in an array
Say that you have a value that you want to iterate, but it may or may not already be an array.
The more explicit (and arguably more readable) approach would be to test the value beforehand with an explicit Array.isArray check:
const theLongWay = (maybeAnArray) => {
if (Array.isArray(maybeAnArray)) return maybeAnArray;
else return [ maybeAnArray ];
};
...or you could just use Array.prototype.flat:
const wrapInArray = (maybeAnArray) => [ maybeAnArray ].flat();
// ^ much shorter ^
wrapInArray( 'a string' );
// -> [ 'a string' ]
wrapInArray( [ 'a', 'list', 'of', 'strings' ] );
// -> [ 'a', 'list', 'of', 'strings' ]
Note that .flat only collapses the outer most array — any already nested arrays are preserved:
wrapInArray( [ ['several'], ['nested'], ['lists'] ] );
// -> [ [ 'several' ], [ 'nested' ], [ 'lists' ] ]
Deep destructuring
Sometimes you don't need the entire return value from something, just an object property here or an array item there.
Destructuring just what you need can make for clean and elegant code.
// connect to a database and query for something
import { Client } from 'pg'
const client = await new Client().connect();
// get only the rows and ignore the rest of the result
const { rows } = await client.query('SELECT "id", "name", "email" FROM "Users"');
// rows -> [ {id, name, email}, {...}, ... ]
But what if you need deeply nested objects and/or arrays? You might be surprised how deep you can go.
// get only the first row and ignore the rest
const { rows: [ firstRow ] = await client.query('SELECT "id", "name" FROM "Products"');
// firstRow -> {id, name}
// get first _property_ of first row
const { rows: [ { count } ] } = await client.query('SELECT COUNT(*) FROM "Products"');
// count -> '99'
const { rows: [ { listCol: [ , secondItem ] } ] } = await client.query(`SELECT json_build_array('foo', 'bar', 'baz') AS "listCol"`);
// secondItem -> 'bar'
Destructuring to existing variables
You'll typically declare a new variable as you destructure into it, but you can actually use an already declared variable!
The trick is wrapping the entire assignment in parentheses, to group it into an expression the compiler recognizes.
let valueInOuterScope;
try {
// without a declaration (const, let), throws a SyntaxError
{ result: valueInOuterScope } = funcThatCouldFail();
// 👍
( { result: valueInOuterScope } = funcThatCouldFail() );
} catch (err) {
console.error('something went wrong:', err);
}
Top comments (0)