DEV Community

Cover image for Stupid Javascript Tricks
Evan K
Evan K

Posted on

Stupid Javascript Tricks

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(' ');
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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 ];
};
Enter fullscreen mode Exit fullscreen mode

...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' ]
Enter fullscreen mode Exit fullscreen mode

Note that .flat only collapses the outer most array — any already nested arrays are preserved:

wrapInArray( [ ['several'], ['nested'], ['lists'] ] );
// -> [ [ 'several' ], [ 'nested' ], [ 'lists' ] ]
Enter fullscreen mode Exit fullscreen mode

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}, {...}, ... ]
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)