What is the difference between debounce
and throttle
?
When might you use debounce
and when might you use throttle
?
// Write debounce, then debounce a scroll event | |
function onScrollFunction(arg) { | |
console.log('scrolling ' + arg); | |
} | |
function debounce(fn, wait) { | |
let timeout; | |
return function(...args) { | |
clearTimeout(timeout); | |
timeout = setTimeout(() => fn.call(this, ...args), wait) | |
}; | |
} | |
const debounced = debounce(onScrollFunction, 1000); | |
window.addEventListener('scroll', () => debounced(1)); |
// Given two identical DOM tree structures, A and B, and a node from A, find the corresponding node in B | |
/* | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width"> | |
<title>Facebook DOM Traversal</title> | |
</head> | |
<body> | |
<div id="rootA"> | |
<div> | |
<div></div> | |
</div> | |
<div></div> | |
<div> | |
<div> | |
<div id="nodeA"></div> | |
<div></div> | |
</div> | |
</div> | |
</div> | |
<div id="rootB"> | |
<div> | |
<div></div> | |
</div> | |
<div></div> | |
<div> | |
<div> | |
<div id="nodeB"></div> | |
<div></div> | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> | |
*/ | |
const rootA = document.getElementById('rootA'); | |
const rootB = document.getElementById('rootB'); | |
const nodeA = document.getElementById('nodeA'); | |
const nodeB = document.getElementById('nodeB'); | |
function getPath(root, node) { | |
const path = []; | |
while (node !== root) { | |
const parent = node.parentElement; | |
const children = Array.from(parent.children); | |
const nodeIndex = children.indexOf(node); | |
path.push(nodeIndex); | |
node = parent; | |
} | |
return path; | |
} | |
function getNodeFromPath(node, path) { | |
const toWalk = [...path]; | |
while (toWalk.length > 0) { | |
node = node.children[toWalk.pop()]; | |
} | |
return node; | |
} | |
function getSymmetricNode(rootA, rootB, nodeA) { | |
const pathToNode = getPath(rootA, nodeA); | |
return getNodeFromPath(rootB, pathToNode); | |
} | |
const targetNode = getSymmetricNode(rootA, rootB, nodeA); | |
console.log(nodeB === targetNode); |
/* Create an event emitter that goes like this | |
* emitter = new Emitter(); | |
* | |
* Allows you to subscribe to some event | |
* sub1 = emitter.subscribe('function_name', callback1); | |
* (you can have multiple callbacks to the same event) | |
* sub2 = emitter.subscribe('function_name', callback2); | |
* | |
* You can emit the event you want with this api | |
* (you can receive 'n' number of arguments) | |
* sub1.emit('function_name', foo, bar); | |
* | |
* And allows you to release the subscription like this | |
* (but you should be able to still emit from sub2) | |
* sub1.release(); | |
*/ | |
class Emitter { | |
constructor() { | |
this.events = {}; | |
} | |
subscribe(eventName, callback) { | |
// Initialise event handlers if event does not exist yet | |
if (!this.events[eventName]) { | |
this.events[eventName] = []; | |
} | |
// Add callback function to a list of functions to be | |
// invoked when this event is emitted | |
this.events[eventName].push(callback); | |
// Return a function to remove this function from being | |
// from the list of functions to be invoked when this | |
// event is emitted | |
return { | |
release: () => { | |
this.events[eventName] = this.events[eventName] | |
.filter((eventFunction) => eventFunction !== callback); | |
} | |
} | |
} | |
emit(eventName, ...parameters) { | |
if (this.events[eventName]) { | |
this.events[eventName] | |
.forEach((eventFunction) => eventFunction(...parameters)); | |
} | |
} | |
} | |
const emitter = new Emitter(); | |
const sub1 = emitter.subscribe('test', (arg) => console.log('sub1', arg)); | |
const sub2 = emitter.subscribe('test', (arg1, arg2) => console.log('sub2', arg1, arg2)); | |
emitter.emit('test', 1); | |
sub1.release(); | |
emitter.emit('test', 1, 2); |
// Given an array of objects, O, and an object of properties, P, write a function that exlcudes the properties from P in O | |
function createExcludedMap(excludes) { | |
return excludes.reduce((map, pair) => { | |
const values = map.get(pair.k); | |
if (values) { | |
values.push(pair.v); | |
return map.set(pair.k, values); | |
} | |
return map.set(pair.k, [pair.v]); | |
}, new Map()); | |
} | |
/* | |
function excludeItems(items, excludes) { | |
excludes.forEach((pair, outer) => { | |
items = items.filter((item, inner) => { | |
return item[pair.k] !== pair.v; | |
}); | |
}); | |
return items; | |
} | |
*/ | |
/* | |
function excludeItems(items, excludes) { | |
const included = []; | |
for (const item of items) { | |
let excluded = false; | |
for (const [key, values] of excludesMap) { | |
if (item[key] && values.includes(item[key])) { | |
excluded = true; | |
break; | |
} | |
} | |
if (!excluded) { | |
included.push(item); | |
} | |
} | |
return included; | |
} | |
*/ | |
function excludeItems(items, excludes) { | |
for (const item of items) { | |
for (const [key, values] of excludesMap) { | |
items = items.filter((item) => !values.includes(item[key])); | |
} | |
} | |
return items; | |
} | |
const items = [ | |
{ color: 'red', type: 'tv', age: 18 }, | |
{ color: 'red', type: 'phone', age: 20 }, | |
{ color: 'silver', type: 'tv', age: 18 }, | |
{ color: 'silver', type: 'phone', age: 20 } | |
]; | |
const excludes = [ | |
{ k: 'color', v: 'red' }, | |
{ k: 'color', v: 'blue' }, | |
{ k: 'type', v: 'phone' }, | |
]; | |
/* | |
const excludesMap = new Map( | |
['color', ['red', 'blue']], | |
['type', ['phone']] | |
); | |
*/ | |
const excludesMap = createExcludedMap(excludes); | |
console.log('result', excludeItems(items, excludesMap)); |
// Write array flatten to arbitrary depth, with recursion and without. | |
/* | |
Inputs | |
------- | |
(1): Empty array | |
-- | |
[] | |
=> [] | |
(2): Flattened already | |
-- | |
[1] | |
=> [1] | |
(3): One level deep | |
-- | |
[[1]] | |
=> [1] | |
(4): Mixed with one level deep | |
-- | |
[1, [2]] | |
=> [1, 2] | |
(5): One level deep multiple arrays | |
-- | |
[[1, 2], [3, 4]] | |
=> [1, 2, 3, 4] | |
(6): Two levels deep | |
-- | |
[[1, [2, 3]], [4, [5, 6, 7]]] | |
=> [1, 2, 3, 4, 5, 6, 7] | |
(7): Arbitrary depth | |
-- | |
[1, [2, [[3]], [4, 5, 6]], [[[[[[7, 8, 9]]]]]]] | |
=> [1, 2, 3, 4, 5, 6, 7, 8, 9] | |
*/ | |
function flatten(arr) { | |
return arr.reduce((flatArray, current) => { | |
if (Array.isArray(current)) { | |
return flatArray.concat(flatten(current)); | |
} | |
flatArray.push(current); | |
return flatArray; | |
}, []); | |
} | |
function flattenIterative(arr) { | |
const flatArray = []; | |
let original = [...arr]; | |
while (original.length > 0) { | |
const current = original.shift(); | |
if (Array.isArray(current)) { | |
original = current.concat(original); | |
} else { | |
flatArray.push(current); | |
} | |
} | |
return flatArray; | |
} | |
const input1 = []; | |
console.log('result', flatten(input1)); | |
console.log('result', flattenIterative(input1)); | |
const input2 = [1]; | |
console.log('result', flatten(input2)); | |
console.log('result', flattenIterative(input2)); | |
const input3 = [[1]]; | |
console.log('result', flatten(input3)); | |
console.log('result', flattenIterative(input3)); | |
const input4 = [1, [2]]; | |
console.log('result', flatten(input4)); | |
console.log('result', flattenIterative(input4)); | |
const input5 = [[1, 2], [3, 4]]; | |
console.log('result', flatten(input5)); | |
console.log('result', flattenIterative(input5)); | |
const input6 = [[1, [2, 3]], [4, [5, 6, 7]]]; | |
console.log('result', flatten(input6)); | |
console.log('result', flattenIterative(input6)); | |
const input7 = [1, [2, [[3]], [4, 5, 6]], [[[[[[7, 8, 9]]]]]]]; | |
console.log('result', flatten(input7)); | |
console.log('result', flattenIterative(input7)); |