- Create a new array with
n
elements, each initialized to x
- Create a new array with
n
elements, each initialized as f()
// Imperatively
const a = []
for (let i = 0; i < n; i++) a[i] = f()
// Using map()
new Array(n).fill().map(f) // fill() is needed to initialize all the elements
// For example, to generate an array of 10 random numbers from 0 to 1
new Array(10).fill().map(Math.random)
- Sum elements in array
arr
arr.reduce((a, b) => a + b, 0)
// or, if arr is known to have at least 1 element,
arr.reduce((a, b) => a + b)
- Count unique elements in an array
arr
- Destructure last element of array
// like const [..._, last] = arr
const [last] = arr.slice(-1)
- Generate all permutations of
arr
in place (i.e. values returned by the iterator are all arr
, but with the items in a different order each time)
const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]]
function* permute(arr, startIndex = 0) {
if (startIndex === arr.length) {
yield arr // or arr.slice() if you want to store the permutations
return
}
for (let swapIndex = startIndex; swapIndex < arr.length; swapIndex++) {
swap(arr, startIndex, swapIndex)
yield* permute(arr, startIndex + 1)
swap(arr, startIndex, swapIndex)
}
}
for (const permutation of permute(arr)) {
/* process permutation */
}
- Map pairs of keys to values
const map = new Map // Map<K1, Map<K2, V>>
// To map (key1, key2) to val
let map1 = map.get(key1)
if (!map1) {
map1 = new Map
map.set(key1, map1)
}
map1.set(key2, val)
;// To get what (key1, key2) is mapped to
(map.get(key1) || new Map).get(key2)
- Keeping track of counts, defaulting to
0
const counts = new Map
// To increment count of item
counts.set(item, (counts.get(item) || 0) + 1)
// To get count of item
counts.get(item) || 0
- Setting keys on objects from variables of the same name
// Explicitly setting field
obj.someLongName = someLongName
// Using Object.assign
Object.assign(obj, {someLongName})
// Immutably
obj = {...obj, someLongName}
- Normally function parameters are passed by position, which requires remembering the order to pass them.
However, destructuring makes it easy to pass parameters by value:
// With positional parameters
const foldl = (f, initial, list) =>
list ? foldl(f, f(initial, list.head), list.tail) : initial
foldl((a, b) => a + b, 0, new LinkedList([1, 2, 3, 4]))
// With named parameters
const foldl = ({f, initial, list}) =>
list ? foldl({f, initial: f(initial, list.head), list: list.tail}) : initial
foldl({
list: new LinkedList([1, 2, 3, 4]),
initial: 0,
f: (a, b) => a + b
})
- You can also easily provide default values for any number of parameters, allowing each one to be provided or omitted:
function fun(required1, {optional1 = 1, optional2 = 2, optional3 = 3} = {}) {
// ...
}
fun('abc')
fun('abc', {optional1: 10})
fun('abc', {optional3: 30})
fun('abc', {optional3: 30, optional2: 20})
- Factorial function (
n => n!
); don't pass it negative numbers
const fac = n => n < 2 ? 1 : n * fac(n - 1)
- Treating a method as a function
const chunks = []
// Writing new anonymous function
stream.on('data', chunk => chunks.push(chunk))
// Tempting, but doesn't work
stream.on('data', chunks.push) // this is like calling (chunks.push)(chunk)
// Binding this param
stream.on('data', chunks.push.bind(chunks))
- Adding block scope to
case
statements
// Naively, without block scope -> SyntaxError: Identifier 'someVar' has already been declared
switch (val) {
case 1:
const someVar = 2
break
case 2:
const someVar = 1
break
// ...
}
// With added block scopes
switch (val) {
case 1: {
const someVar = 2
break
}
case 2: {
const someVar = 1
break
}
// ...
}
- Ignoring results from a request if a newer one has been issued
let searchToken
function search() {
const token = Symbol()
searchToken = token
somethingAsynchronous(response => {
if (searchToken !== token) return
// process response
})
}