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)); |