133
\$\begingroup\$

What general tips do you have for golfing in JavaScript? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to JavaScript (e.g. "remove comments" is not an answer).

Note: Also see Tips for Golfing in ECMAScript 6 and above

\$\endgroup\$
  • \$\begingroup\$ I was actually wondering, is it allowed to put variables in global (saves var)? And should JavaScript golf code be a function or output something directly? I honestly think this can make much difference. \$\endgroup\$ – pimvdb May 27 '11 at 5:28
  • 1
    \$\begingroup\$ @primvdb: It is allowed, but you have to be careful because it can cause side-effects if a function is called multiple times and it is manipulating global variables, or if it is a recursive function. \$\endgroup\$ – mellamokb May 27 '11 at 13:44
  • \$\begingroup\$ This link has a bunch of tips relevant here. \$\endgroup\$ – Isiah Meadows Feb 12 '15 at 4:53

78 Answers 78

2
\$\begingroup\$

Stack Ternary Operators

Need to test many conditions, try this:

a ? b : c ? d : e ? f : g

Use the comma operator

Using an arrow function, and need to return something else than what your doing? Feel no need to break out the {b;return a} and instead use the comma operator:

   f=>{f.map(b=>...);return a}
   f=>(f.map(b=>...),a)

Take advantage of =

Assignment without var can be vital to shave off bytes. Since it returns the value, you can:

Assign variables in function calls:

Array(100).fill(100);p=100
Array(p=100).fill(p)

Stack variable assignment:

a=1,b=1,c=1
a=b=c=1

Assign variables in control structures:

if(b=1) {...}

Since 1 is truthy (If tested against a boolean it will convert to true), the block will run

\$\endgroup\$
  • 1
    \$\begingroup\$ I feel like most of these tips are already somewhere else in the thread (yes, this feels like a thread, not a question). Please remove the redundant ones. \$\endgroup\$ – lirtosiast Oct 16 '15 at 2:52
2
\$\begingroup\$

Use atob() and btoa() to compress/decompress strings

alert('adifonoiewnfqowinfiodnasfoinqeiwnfqoiwnfoiansdfoinqowfe') //before
alert(btoa('iØ¢z"{ ߪ"ø¨vv¬~§©è°ú¨    ߢ&§±×èz¨Á÷')) //after

Great for some challenges. Only works on strings with a-zA-Z0-9 and no other chars.

\$\endgroup\$
  • \$\begingroup\$ +/ are allowed in the string as well. Also, you can sometimes use a .replace to add back in incompatible chars while still saving bytes. \$\endgroup\$ – ETHproductions Dec 22 '15 at 1:25
  • \$\begingroup\$ One more thing: this doesn't work for strings of all lengths. \$\endgroup\$ – ETHproductions Feb 12 '16 at 17:25
  • 2
    \$\begingroup\$ Oh, and JavaScript supports ISO-8859-1, so you can count every char in the compressed string as 1 byte. \$\endgroup\$ – ETHproductions Dec 13 '16 at 18:22
  • \$\begingroup\$ Waaaait...... For real? I thought JS used UCS-2/UTF-16 for encoding. Is there an example of this working? \$\endgroup\$ – Mama Fun Roll Dec 13 '16 at 22:44
  • \$\begingroup\$ Here's what I did: 1. Create a .js file that contains some chars in the range 0xA0-0xFF and save it in the ISO-8859-1 encoding. 2. Create a .html file that calls the JS file via a <script src="myfile.js"> tag. 3. Open the HTML file in any browser. I'm not sure if this proves that JS supports ISO-8859-1, but that's the best method I can think of... \$\endgroup\$ – ETHproductions Dec 13 '16 at 23:00
2
\$\begingroup\$

Adding Values with Implicit Casting

Improved zzzzBov solution:

//not so good
-(-a-b)==c;

//best
a- -b==c;
a-+-b==c;

We save 2 characters by using these solutions.

\$\endgroup\$
  • \$\begingroup\$ +"10"+ +"5"===15 \$\endgroup\$ – gion_13 Mar 14 '12 at 9:59
  • 2
    \$\begingroup\$ Note that the space is needed because the -- (decrement) operator takes precedence over subtraction. Also, @gion_13, what's the point? your solution has one extra character. \$\endgroup\$ – Camilo Martin Dec 18 '12 at 13:18
  • 1
    \$\begingroup\$ You can replace the space with a + (e.g. a-+-b) \$\endgroup\$ – Toothbrush Nov 5 '15 at 16:37
  • 2
    \$\begingroup\$ In this case use a==c-b \$\endgroup\$ – l4m2 Apr 13 '18 at 16:24
  • \$\begingroup\$ maybe that can work, in some cases, but i don't think they are interchangeable on all scenarios, but very cool! \$\endgroup\$ – ajax333221 Apr 27 '18 at 23:16
2
\$\begingroup\$

Use Bitwise as Logic Operators When Dealing With Booleans

a = 1 //although this would usually be a boolean expression
b = 0 //same
if(a&&b)c()
if(a&b)c()

Then, use && lazy evaluation to make a chain of ampersands:

a&b&&c()
if(a&&b)c()

Saves 3 characters

\$\endgroup\$
  • \$\begingroup\$ You could use 0 and 1 instead of booleans. Again, semicolons are optional. \$\endgroup\$ – mbomb007 May 21 '15 at 19:37
  • \$\begingroup\$ I know, these were just examples. Instead of true and false, there would most likely be expressions. I used variables to illustrate my point. \$\endgroup\$ – Cyoce May 21 '15 at 21:33
2
\$\begingroup\$

Function

If you need a function in as few bytes as possible, and any function will do (perhaps you just want to access some of the goodies from Function.prototype), then here are some options (starting with large ones):

Function.prototype
[].map
Date
CSS     (available in modern browsers)
Map     (ES6: available in Node and modern browsers)
Set     (ES6: available in Node and modern browsers)
URL     (available in very old browsers, but not in Node)

So if you want a reference to the call function, you can get it like this:

c=URL.call
\$\endgroup\$
2
\$\begingroup\$

ES6-specific: avoid Function#bind()

Self-explanatory, 7 bytes saved

f.bind(null,x,...xs)
_=>f(x,...xs)

Use sloppy mode to avoid variable declarations

Here, 8 bytes saved

a.forEach(e=>{let b=e+1,c=d(b)+2;f(e);g(b);h(c);i(b,c)})
a.forEach(e=>{f(e);g(b=e+1);h(c=d(b)+2);i(b,c)})

// Even better, reuse variables and use `Array#map()`
// Drops an additional 6 bytes
a.map(e=>{f(e++);g(e);h(b=d(e)+2);i(e,b)})

This also holds in ES5. Here, 8 bytes saved

a.forEach(function(e){let b=e+1,c=d(b)+2;f(e);g(b);h(c);i(b,c)})
a.forEach(function(e){f(e);g(b=e+1);h(c=d(b)+2);i(b,c)})

// Even better, reuse variables and use `Array#map()`
a.map(function(e){f(e++);g(e);h(b=d(e)+2);i(e,b)})
\$\endgroup\$
  • 1
    \$\begingroup\$ 1. You don't ever need to initialize variables in code-golf unless you want them to be undefined, so getting rid of let saves 4 bytes off of the first line of both examples. 2. If you want to use .bind with customizable parameters, .bind is better: q=f.bind(0,x) vs. q=(..._)=>f(x,..._) (though if you only want one parameter, q=y=>f(x,y) is better). \$\endgroup\$ – ETHproductions Dec 3 '16 at 20:12
  • \$\begingroup\$ @ETHproductions Updated \$\endgroup\$ – Isiah Meadows Dec 3 '16 at 20:39
2
\$\begingroup\$

If you are ever in a situation where you have an array a and you need to get its last element as an array of itself, use Array#slice():

[a[a.length-1]]    // 15
a.slice(-1)        // 11
\$\endgroup\$
  • 2
    \$\begingroup\$ And, if you don't mind changing the original array, [a.pop()], at 9 characters. \$\endgroup\$ – Tomas Langkaas Sep 8 '17 at 16:53
2
\$\begingroup\$

This is one of my favorites - ES6

'da,dad,sa'.split``
 ["d", "a", ",", "d", "a", "d", ",", "s", "a"]

'da,dad,sa'.split`,`
["da", "dad", "sa"]
\$\endgroup\$
  • 3
    \$\begingroup\$ The first one [...'asdasd'] \$\endgroup\$ – l4m2 Apr 25 '18 at 6:34
  • \$\begingroup\$ Did you copy and paste ? it, if done in the console and in code pen I get ["d", "a", ",", "d", "a", "d", ",", "s", "a"] 'da,dad,sa'.split`` (9) ["d", "a", ",", "d", "a", "d", ",", "s", "a"] \$\endgroup\$ – Newbie programmer Apr 26 '18 at 13:26
  • \$\begingroup\$ [...'da,dad,sa'] ... \$\endgroup\$ – Cétia Jun 27 '18 at 15:58
2
\$\begingroup\$

If you are using the same function more than once, it's often useful to reference the function to a new function name. I.e:

Instead of:

Math.sqrt(4)
Math.sqrt(16)
Math.sqrt(100)

you can do:

r=Math.sqrt
r(4)
r(16)
r(100)
\$\endgroup\$
  • 6
    \$\begingroup\$ Correct, but that is not JavaScript specific, is a generic tip added to Tips for golfing in <all languages> by Blazer more that 6 years ago. \$\endgroup\$ – manatwork Jun 26 '18 at 8:32
  • \$\begingroup\$ Welcome to PPCG! As manatwork pointed out this tip has already be posted, so I recommend deleting it. But I hope you nevertheless stick around and answer some challenges. :) \$\endgroup\$ – Laikoni Jun 26 '18 at 12:55
  • 2
    \$\begingroup\$ 4**.5;16**.5;100**.5 is even shorter. \$\endgroup\$ – Dennis Jun 26 '18 at 14:53
  • \$\begingroup\$ Thanks for your replies. I will take a look at the general tips. Maybe I find something which has not yet been added ;) love code golfing though! \$\endgroup\$ – Xzibitee Jun 29 '18 at 11:57
2
\$\begingroup\$

Use the simplest shortening method available - your variable declaration!

var myName = "Jack";

Obviously is very long compared to:

m="Jack"

It's a whole 12 characters shorter. You have all 64 of these single-character variable names available:

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_

And also - never use spaces or semicolons in your variable naming - changing m = "Mum"; to m="Mum" saves another three characters.

\$\endgroup\$
1
\$\begingroup\$

Array#concat() and the spread operator

This largely depends on the situation.


Combining multiple arrays.

Prefer the concat function unless cloning.

0 bytes saved

a.concat(b)
[...a,...b]

3 bytes wasted

a.concat(b,c)
[...a,...b,...c]

3 bytes saved

a.concat()
[...a]

6 bytes saved

// Concatenate array of arrays
[].concat.apply([],l)
[].concat(...l)

Prefer using an already existing array to Array#concat().

Easy 4 bytes saved

[].concat(a,b)
a.concat(b)
\$\endgroup\$
  • \$\begingroup\$ Can't see the point of all this. Concat works with multiple array parameters, does not need apply or spread. Example 1: a.concat(b,c,d,e,f) \$\endgroup\$ – edc65 Feb 11 '15 at 0:09
  • \$\begingroup\$ Flatten: let f=l=>[].concat.apply(l.map(x=>Array.isArray(x)?f(x):x)) vs let f=l=>[...l.map(x=>Array.isArray(x)?f(x):x)] \$\endgroup\$ – Isiah Meadows Feb 11 '15 at 1:56
  • \$\begingroup\$ Well...except my flatten shouldn't work. Here's a corrected version: let f=x=>[].concat(...l.map(x=>Array.isArray(x)?f(x):x)). The previous version would've done nothing. \$\endgroup\$ – Isiah Meadows Feb 12 '15 at 3:53
  • \$\begingroup\$ It's still 6 bytes saved. \$\endgroup\$ – Isiah Meadows Feb 12 '15 at 3:53
1
\$\begingroup\$

You can check if a value is *truish by simply passing it:

if(val){...}

*everything different than 0, "", false, null, undefined and NaN is evaluated to true !

This method can be applied with many other functions and operators:

  • ternary operator val?"true":"false";
  • for loop for(;val;){...}
  • while loop while(val){...}
  • etc...
\$\endgroup\$
  • \$\begingroup\$ Or val&&.... Now, it is not equivalent to val == true. Example: 0 != true. \$\endgroup\$ – Isiah Meadows Feb 12 '15 at 4:56
  • \$\begingroup\$ All these strings also evaluate to true: '0', ' ', ' 0', '0 ' \$\endgroup\$ – Toothbrush Nov 5 '15 at 16:41
1
\$\begingroup\$

If iterating through own properties, prefer Object.keys.

15 bytes saved

for(let p in o)if(o.hasOwnProperty(e)){/* ... */}
for(let p of Object.keys(o)){/* ... */}
Object.keys(o).map(p=>{/* ... */})

This is also the case for ES5, where it is 7 bytes saved.

for(var p in o)if(o.hasOwnProperty(e)){/* ... */}
Object.keys(o).map(function(p){/* ... */})

If you do that more than once, alias it as a function.

In this ES6 example, 6 bytes saved. It still saves bytes in ES5, but only if used 3 times or more.

Object.keys(o).map(p=>{/* ... */})Object.keys(o).map(p=>{/* ... */})
i=f=>Object.keys(o).map(f);i(p=>{/* ... */});i(p=>{/* ... */})
\$\endgroup\$
1
\$\begingroup\$

When stringifying dates, .toJSON saves 5 bytes over .toISOString. Apparently this was supported as far back as Firefox 4, but this answer is only the sixth on PPCG to mention it.

\$\endgroup\$
1
\$\begingroup\$

Shortening Promise Chains with async/await

Sometimes you can shorten longer promise chains with async/await. The main benefit is from getting rid of the beginning of the arrow function in each then callback. .then(x=>x (10) gets replaced with await( (-4), but you first pay with async (+6). So to make up for the initial overhead of 6 bytes, you'd need at least two then chains to get any benefit.

+-------------+----------------+
| then chains | async overhead |
+-------------+----------------+
| 0           | +6             |
| 1           | +2             |
| 2           | -2             |
| 3           | -4             |
| …           | …              |
+-------------+----------------+

Example 1

x=>x().then(y=>y.foo()).then(z=>z.bar())
async x=>await(await(x()).foo()).bar()

Example 2

u=>fetch(u).then(r=>r.text()).then(t=>/\0/.test(t))
async u=>/\0/.test(await(await fetch(u)).text()))
\$\endgroup\$
0
\$\begingroup\$

Remove duplicates from array

a.filter(e=>!(t[e]=e in t)) 

let unique= (a,t={})=> a.filter(e=>!(t[e]=e in t));

// "stand-alone" version working with global t:
// a1.filter((t={},e=>!(t[e]=e in t)));

// Test data
let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];
let a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];
let a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];

// Results
console.log(JSON.stringify( unique(a1) ))
console.log(JSON.stringify( unique(a2) ))
console.log(JSON.stringify( unique(a3) ))

O(n) performance; we assume your array is in a and t={}. Explanation here

And shorter but slower version (which not work with 2D arrays)

[...new Set(a)]

let unique = a => [...new Set(a)];

// Test data
let a1 = [5, 6, 0, 4, 9, 2, 3, 5, 0, 3, 4, 1, 5, 4, 9];
let a3 = ['Mike', 'Adam', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];

// Results
console.log(JSON.stringify(unique(a1)))
console.log(JSON.stringify(unique(a3)))

\$\endgroup\$
  • \$\begingroup\$ @manatwork So it is, I missed that when reviewing :/ \$\endgroup\$ – caird coinheringaahing Aug 22 at 9:50
0
\$\begingroup\$

Add elements to Array

The concat is more useful than push in many situations because it returns whole array e.g. we want from d=[{a:1,b:2,c:3},{a:7,b:8,c:9}] generate 'pivot object' p={"a":[1,7],"b":[2,8],"c":[3,9]}

let p={}, d=[{a:1,b:2,c:3},{a:7,b:8,c:9}]; 

d.map(x=> Object.keys(x).map(k=> p[k]= (p[k]||[]).concat(x[k]) ))

console.log(JSON.stringify(p));

\$\endgroup\$
  • 2
    \$\begingroup\$ Instead of mapping the keys, you can do for(k in x) and, instead of the concatenation, you can do p[k]=[...p[k]||[],x[k]]. After wrapping that in {}s, it saves you 14 bytes. TIO \$\endgroup\$ – Shaggy Sep 17 at 9:37
  • \$\begingroup\$ @Shaggy thanks :) - I don't know that tricks. May be you have also some suggestion to this? \$\endgroup\$ – Kamil Kiełczewski Sep 17 at 10:10
-3
\$\begingroup\$

Converting a string to an int/float by subtracting a empty array

Before(22 bytes)

parseFloat("12.52463")

After(13 bytes, saved 9 bytes)

"12.52463"-[]
\$\endgroup\$
  • 12
    \$\begingroup\$ This is covered by other answers already. The shortest version is +"12.52463". \$\endgroup\$ – Martin Ender Feb 10 '15 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.