From 53e195884f4ddd5b99c2e395a4dd62e39440a24d Mon Sep 17 00:00:00 2001 From: ALEGATOR1209 Date: Sun, 4 Nov 2018 14:01:44 +0200 Subject: [PATCH 1/6] Done homework --- JavaScript/8-timer.js | 47 +++++++++++++ JavaScript/9-least.js | 48 ++++++++++++++ JavaScript/a-bytes.js | 86 ++++++++++++++++++++++++ JavaScript/b-universal.js | 54 +++++++++++++++ JavaScript/c-object.js | 134 ++++++++++++++++++++++++++++++++++++++ README.md | 32 ++++----- 6 files changed, 385 insertions(+), 16 deletions(-) create mode 100644 JavaScript/8-timer.js create mode 100644 JavaScript/9-least.js create mode 100644 JavaScript/a-bytes.js create mode 100644 JavaScript/b-universal.js create mode 100644 JavaScript/c-object.js diff --git a/JavaScript/8-timer.js b/JavaScript/8-timer.js new file mode 100644 index 0000000..601c712 --- /dev/null +++ b/JavaScript/8-timer.js @@ -0,0 +1,47 @@ +'use strict'; + +const memoize = (fn, msec = 100) => { + let cache = {}; + let timer = setTimeout(() => { + console.log('Cache cleaned'); + timer = null; + cache = {}; + }, msec); + + const setTimer = msec => { + timer = setTimeout(() => { + console.log('Cache cleaned'); + timer = null; + cache = {}; + }, msec); + }; + + const generateKey = (...args) => args.join('|'); + + return (...args) => { + if (!timer) setTimer(msec); + const key = generateKey(...args); + + if (cache[key]) { + console.log(`From cache: ${args} = ${cache[key]}`); + return cache[key]; + } + + console.log(`Calculate: ${args} = ${fn(...args)}`); + const res = fn(...args); + cache[key] = res; + console.log(cache); + return res; + }; +}; + +const sum = (a, b) => a + b; + +const sumM = memoize(sum, 2000); + +//USAGE +sumM(1, 2); +sumM(1, 2); +setTimeout(() => { + sumM(1, 2); +}, 2500); diff --git a/JavaScript/9-least.js b/JavaScript/9-least.js new file mode 100644 index 0000000..c9460aa --- /dev/null +++ b/JavaScript/9-least.js @@ -0,0 +1,48 @@ +'use strict'; + +const memoizeLimited = (fn, size) => { + const cache = {}; + + const generateKey = (...args) => args.join('|'); + + const leastUsed = () => { + let toDelete = Object.keys(cache)[0]; + for (const i in cache) { + if (cache[i].uses < cache[toDelete].uses) { + toDelete = i; + } + } + console.log('Deleting', toDelete); + return toDelete; + }; + + return (...args) => { + const key = generateKey(...args); + + if (cache[key]) { + console.log(`From cache: ${args}`); + cache[key].uses++; + return cache[key].value; + } + + if (Object.keys(cache).length === size) + delete cache[leastUsed()]; + + const res = { value: fn(...args), uses: 0 }; + console.log(`Calculating ${args}`); + cache[key] = res; + return cache[key].value; + }; +}; + +//USAGE + +const sum = (a, b) => a + b; +const sumM = memoizeLimited(sum, 3); + +console.log(`sumM(1, 2) = ${sumM(1, 2)}`); //uses: 0 +console.log(`sumM(2, 3) = ${sumM(2, 3)}`); //uses: 0 +console.log(`sumM(1, 2) = ${sumM(1, 2)}`); //uses: 1 +console.log(`sumM(2, 3) = ${sumM(2, 3)}`); //uses: 2 +console.log(`sumM(1, 3) = ${sumM(1, 3)}`); //uses: 0 +console.log(`sumM(5, 6) = ${sumM(5, 6)}`); //uses: 0 diff --git a/JavaScript/a-bytes.js b/JavaScript/a-bytes.js new file mode 100644 index 0000000..7f830de --- /dev/null +++ b/JavaScript/a-bytes.js @@ -0,0 +1,86 @@ +'use strict'; + +const memoizeBytes = (fn, size) => { + let cache = {}; + + const generateKey = (...args) => args.join('|'); + + const sizeToBytes = (size) => { + const length = size.length; + const data = parseFloat(size, 10); + let k = 1; + switch (size.charAt(length - 1)) { + case 'T': k *= 1000; + case 'G': k *= 1000; + case 'M': k *= 1000; + case 'K': k *= 1000; + case 'B': break; + default: break; + } + return data * k; + }; + + const sizeInBytes = sizeToBytes(size); + + const getSize = (obj) => { + let bytes = 0; + for (const i in obj) { + switch (typeof obj[i]) { + case 'number': + bytes += 8; + break; + case 'boolean': + bytes += 4; + break; + case 'string': + bytes += obj[i].length * 2; + break; + case 'object': + bytes += getSize(obj[i]); + break; + default: throw new Error('getSize: Wrong field type.'); + } + } + return bytes; + }; + + return (...args) => { + const key = generateKey(...args); + + if (cache[key]) { + console.log(`From cache: ${key}`); + return cache[key]; + } + + if (getSize(cache) >= sizeInBytes) { + console.log(`Cache with size ${getSize(cache)} bytes cleaned`); + cache = {}; + } + + console.log(`Calculating: ${key}`); + cache[key] = fn(...args); + console.dir(`Cache size: ${getSize(cache)}`); + return fn(...args); + }; +}; + +//USAGE + +const sum = (a, b) => a + b; +const sumB = memoizeBytes(sum, '32B'); +console.log(`sumB(1, 2) = ${sumB(1, 2)}`); +console.log(`sumB(1, 2) = ${sumB(1, 2)}`); +console.log(`sumB(2, 3) = ${sumB(2, 3)}`); +console.log(`sumB(3, 2) = ${sumB(3, 2)}`); +console.log(`sumB(4, 2) = ${sumB(4, 2)}`); +console.log(`sumB(2, 3) = ${sumB(2, 3)}`); +console.log(`sumB(1, 3) = ${sumB(1, 3)}`); +console.log(`sumB(4, 3) = ${sumB(4, 3)}`); +console.log(`sumB(9, 3) = ${sumB(9, 3)}`); +console.log(`sumB(8, 3) = ${sumB(8, 3)}`); + +const sumK = memoizeBytes(sum, '0.25K'); +const arr = []; +for (let i = 0; i < 100; i++) {; + arr[i] = sumK(i, i-1); +} \ No newline at end of file diff --git a/JavaScript/b-universal.js b/JavaScript/b-universal.js new file mode 100644 index 0000000..90e2826 --- /dev/null +++ b/JavaScript/b-universal.js @@ -0,0 +1,54 @@ +'use strict'; + +const memeizeUniversal = fn => { + const cache = {}; + const generateKey = (...args) => args.join('|'); + + return (...args) => { + let callback; + if (typeof args[args.length - 1] === 'function') + callback = args.pop(); + const key = generateKey(...args); + + if (cache[key]) { + if (callback) { + console.log(`Async cache: ${key}`); + callback(cache[key].err, cache[key].data); + return; + } + console.log(`Sync cache: ${key}`); + return cache[key]; + } + + if (callback) { + console.log(`Async calc: ${key}`); + fn(...args, (err, data) => { + cache[key] = { err, data }; + callback(err, data); + }); + return; + } + console.log(`Sync calc: ${key}`); + cache[key] = fn(...args); + return cache[key]; + }; +}; + +//USAGE + +const fs = require('fs'); + +const asyncRead = memeizeUniversal(fs.readFile); + +asyncRead('b-universal.js', 'utf8', (err, data) => { + console.log('data length:', data.length); + asyncRead('b-universal.js', 'utf8', (err, data) => { + console.log('data length:', data.length); + }); +}); + +const сум = (a, b) => a + ' => ' + b; +const журба = memeizeUniversal(сум); +console.log(журба('Грушевський', 'Житомир')); +console.log(журба('Скоропадський', 'Мотовилівка')); +console.log(журба('Грушевський', 'Житомир')); diff --git a/JavaScript/c-object.js b/JavaScript/c-object.js new file mode 100644 index 0000000..dd8793f --- /dev/null +++ b/JavaScript/c-object.js @@ -0,0 +1,134 @@ +'use strict'; + +const generateKey = (...args) => args.join('|'); +const getSize = (obj) => { + let bytes = 0; + for (const i in obj) { + switch (typeof obj[i]) { + case 'number': + bytes += 8; + break; + case 'boolean': + bytes += 4; + break; + case 'string': + bytes += obj[i].length * 2; + break; + case 'object': + bytes += getSize(obj[i]); + break; + default: throw new Error('getSize: Wrong field type.'); + } + } + return bytes; +}; + +const memoizator = fn => { + let cache = {}; + const events = []; + + const func = (...args) => { + const key = generateKey(...args); + + if (func.timeout && !func.timer) { + func.timer = setTimeout(() => { + cache = {}; + console.log(`Cache cleared after ${func.timeout} msec`); + }, func.timeout); + } + + if (cache[key]) { + console.log(`From cache ${key}`); + return cache[key]; + } + + if (func.maxSize && getSize(cache) >= func.maxSize) { + console.log(`Cache with size ${getSize(cache)} bytes cleaned`); + cache = {}; + } + + const cacheSize = Object.keys(cache).length; + if (func.maxCount && cacheSize >= func.maxCount) { + console.log(`Cache with length ${cacheSize} cleaned`); + } + + console.log(`Calc ${key}`); + cache[key] = fn(...args); + return cache[key]; + }; + + func.clear = () => { + console.log('Cache cleared'); + cache = {}; + }; + + func.add = (key, value) => { + if (cache[key]) throw new Error('This key already exists'); + else cache[key] = value; + console.log(`${key} added`); + }; + + func.get = (key) => cache[key]; + + func.del = (key) => { + if (cache[key]) { + console.log(`${key} deleted`); + delete cache[key]; + return; + } + console.log(`There is no ${key}`); + }; + + func.on = (name, fn) => { + if (events[name]) throw new Error('Event already exists'); + if (!['clear', 'add', 'del'].find((el) => (el === name))) + throw new Error('Wrong event name'); + events[name] = fn; + }; + + func.emit = (name, data) => { + if (name === 'clear') { + events[name](cache); + func.clear(); + return true; + } + if (name === 'add') { + const res = data.map((el) => events[name](el)); + res.map((el, key) => func.add(key, el)); + return true; + } + if (name === 'del') { + const res = []; + res.push(events[name](...data, cache)); + res.map((key) => func.del(key)); + return true; + } + throw new Error('Frong event name'); + }; + + func.timeout = null; + func.maxSize = null; + func.maxCount = null; + + return func; +}; + +//USAGE + +const sum = (a, b) => a + b; +const sumM = memoizator(sum); +console.log(sumM(1, 1)); +sumM.maxCount = 3; +sumM.timeout = 100; +sumM.maxSize = 32; + +sumM.on('clear', (cache) => console.dir({ cache })); +sumM.emit('clear'); + +const fibbo = (n) => (n < 2 ? 1 : fibbo(n - 1) + fibbo(n - 2)); +sumM.on('add', fibbo); +sumM.emit('add', [1, 2, 3]); + +sumM(1, 2); +sumM.on('del', (el) => generateKey(el, el + 1)); +sumM.emit('del', [1]); diff --git a/README.md b/README.md index be5f0d4..cf14897 100644 --- a/README.md +++ b/README.md @@ -3,19 +3,19 @@ Memoization of synchronous and asynchronous functions Tasks: -- see examples -- implement time expiration cash -- implement memoize with max records count and removing least used -- implement memoize with max total stored data size -- implement universal memoize compatible with both sync and async function -- implement functional object with following properties methods and events: - - `memoized.clear()` - clear cache - - `memoized.add(key, value)` - add value to cach - - `memoized.del(key)` - remove value from cach - - `memoized.get(key)` - returns saved value - - `memoized.timeout: Number` - cache timout - - `memoized.maxSize: Number` - maximum cache size in bytes - - `memoized.maxCount: Number` - maximum cache size in item count - - `memoized.on('add', Function)` - - `memoized.on('del', Function)` - - `memoized.on('clear', Function)` ++ see examples ++ implement time expiration cash ++ implement memoize with max records count and removing least used ++ implement memoize with max total stored data size ++ implement universal memoize compatible with both sync and async function ++ implement functional object with following properties methods and events: + + `memoized.clear()` - clear cache + + `memoized.add(key, value)` - add value to cach + + `memoized.del(key)` - remove value from cach + + `memoized.get(key)` - returns saved value + + `memoized.timeout: Number` - cache timout + + `memoized.maxSize: Number` - maximum cache size in bytes + + `memoized.maxCount: Number` - maximum cache size in item count + + `memoized.on('add', Function)` + + `memoized.on('del', Function)` + + `memoized.on('clear', Function)` From 56fefe5f502bcf3db7ff7d1d35a8e6069a42aaa5 Mon Sep 17 00:00:00 2001 From: ALEGATOR1209 Date: Sun, 4 Nov 2018 14:05:14 +0200 Subject: [PATCH 2/6] a-bytes.js fixed --- JavaScript/a-bytes.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/JavaScript/a-bytes.js b/JavaScript/a-bytes.js index 7f830de..a1d34b9 100644 --- a/JavaScript/a-bytes.js +++ b/JavaScript/a-bytes.js @@ -76,11 +76,5 @@ console.log(`sumB(4, 2) = ${sumB(4, 2)}`); console.log(`sumB(2, 3) = ${sumB(2, 3)}`); console.log(`sumB(1, 3) = ${sumB(1, 3)}`); console.log(`sumB(4, 3) = ${sumB(4, 3)}`); -console.log(`sumB(9, 3) = ${sumB(9, 3)}`); +console.log(`sumB(9, 3) = ${sumB(1, 3)}`); console.log(`sumB(8, 3) = ${sumB(8, 3)}`); - -const sumK = memoizeBytes(sum, '0.25K'); -const arr = []; -for (let i = 0; i < 100; i++) {; - arr[i] = sumK(i, i-1); -} \ No newline at end of file From 7367d48fd38e51280d4e80883883091f7b322989 Mon Sep 17 00:00:00 2001 From: ALEGATOR1209 Date: Fri, 16 Nov 2018 21:06:20 +0200 Subject: [PATCH 3/6] Fixed small problems --- JavaScript/{8-timer.js => 8-timeout.js} | 33 +++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) rename JavaScript/{8-timer.js => 8-timeout.js} (56%) diff --git a/JavaScript/8-timer.js b/JavaScript/8-timeout.js similarity index 56% rename from JavaScript/8-timer.js rename to JavaScript/8-timeout.js index 601c712..b10de77 100644 --- a/JavaScript/8-timer.js +++ b/JavaScript/8-timeout.js @@ -2,34 +2,41 @@ const memoize = (fn, msec = 100) => { let cache = {}; - let timer = setTimeout(() => { - console.log('Cache cleaned'); - timer = null; - cache = {}; - }, msec); const setTimer = msec => { - timer = setTimeout(() => { + let timer = setTimeout(() => { console.log('Cache cleaned'); timer = null; cache = {}; }, msec); + + return timer; }; - const generateKey = (...args) => args.join('|'); + const hasKey = (key) => Object.keys(cache).includes(key); + + const timer = setTimer(msec); + + const generateKey = (args) => { + let key = ''; + for (const arg of args) + key += `${arg}~${typeof arg}|`; + return key; + }; return (...args) => { if (!timer) setTimer(msec); - const key = generateKey(...args); + const key = generateKey(args); - if (cache[key]) { + if (hasKey(key)) { console.log(`From cache: ${args} = ${cache[key]}`); return cache[key]; } console.log(`Calculate: ${args} = ${fn(...args)}`); const res = fn(...args); - cache[key] = res; + if (res !== undefined) + cache[key] = res; console.log(cache); return res; }; @@ -40,8 +47,8 @@ const sum = (a, b) => a + b; const sumM = memoize(sum, 2000); //USAGE -sumM(1, 2); -sumM(1, 2); +sumM(1, -1); +sumM(1, -1); setTimeout(() => { - sumM(1, 2); + sumM(1, -1); }, 2500); From c4a9f226da2cc446f82ac02c4f33c9802cc218e1 Mon Sep 17 00:00:00 2001 From: ALEGATOR1209 Date: Fri, 16 Nov 2018 23:50:46 +0200 Subject: [PATCH 4/6] timeout now deletes only keys which weren't used for some time --- JavaScript/8-timeout.js | 69 +++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/JavaScript/8-timeout.js b/JavaScript/8-timeout.js index b10de77..79e4d4a 100644 --- a/JavaScript/8-timeout.js +++ b/JavaScript/8-timeout.js @@ -1,22 +1,9 @@ 'use strict'; const memoize = (fn, msec = 100) => { - let cache = {}; - - const setTimer = msec => { - let timer = setTimeout(() => { - console.log('Cache cleaned'); - timer = null; - cache = {}; - }, msec); - - return timer; - }; - + const cache = {}; + let timer; const hasKey = (key) => Object.keys(cache).includes(key); - - const timer = setTimer(msec); - const generateKey = (args) => { let key = ''; for (const arg of args) @@ -24,31 +11,67 @@ const memoize = (fn, msec = 100) => { return key; }; - return (...args) => { - if (!timer) setTimer(msec); + const throwGarbage = () => { + const cleaningTime = new Date(); + const removingTime = cleaningTime - msec; + + const toDelete = Object.keys(cache) + .filter(key => cache[key].lastUse >= removingTime); + + for (const key in cache) { + if (toDelete.includes(key)) { + console.log(`${key} deleted.`); + delete cache[key]; + } + if (Object.keys(cache).length === 0) { + console.log('Cache is empty'); + clearInterval(timer); + timer = null; + } + } + }; + + const setTimer = (msec) => { + const timer = setInterval(() => throwGarbage(), msec); + return timer; + }; + + timer = setTimer(msec); + + const func = (...args) => { + if (!timer) timer = setTimer(msec); const key = generateKey(args); if (hasKey(key)) { - console.log(`From cache: ${args} = ${cache[key]}`); - return cache[key]; + console.log(`From cache: ${args} = ${cache[key].value}`); + cache[key].lastUse = new Date(); + console.log(cache); + return cache[key].value; } console.log(`Calculate: ${args} = ${fn(...args)}`); const res = fn(...args); if (res !== undefined) - cache[key] = res; + cache[key] = { value: res, lastUse: new Date() }; console.log(cache); return res; }; + + return func; }; const sum = (a, b) => a + b; -const sumM = memoize(sum, 2000); +const sumM = memoize(sum, 250); //USAGE sumM(1, -1); -sumM(1, -1); +sumM(2, -1); setTimeout(() => { sumM(1, -1); -}, 2500); + setTimeout(() => sumM(2, -1), 100); + setTimeout(() => { + sumM(2, 3); + setTimeout(() => sumM(3, 4), 1000); + }, 500); +}, 200); From d28df6d7887fa4859d7ee55ab02d6e9766b8fd26 Mon Sep 17 00:00:00 2001 From: ALEGATOR1209 Date: Fri, 16 Nov 2018 23:56:15 +0200 Subject: [PATCH 5/6] Oops I've forgot about null --- JavaScript/8-timeout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JavaScript/8-timeout.js b/JavaScript/8-timeout.js index 79e4d4a..aa89832 100644 --- a/JavaScript/8-timeout.js +++ b/JavaScript/8-timeout.js @@ -2,7 +2,7 @@ const memoize = (fn, msec = 100) => { const cache = {}; - let timer; + let timer = null; const hasKey = (key) => Object.keys(cache).includes(key); const generateKey = (args) => { let key = ''; From d19d32683108b9495454c25d8a709d38b675fadd Mon Sep 17 00:00:00 2001 From: ALEGATOR1209 Date: Sat, 17 Nov 2018 10:52:44 +0200 Subject: [PATCH 6/6] Improved a-bytes.js --- JavaScript/a-bytes.js | 74 ++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/JavaScript/a-bytes.js b/JavaScript/a-bytes.js index a1d34b9..8b25c0e 100644 --- a/JavaScript/a-bytes.js +++ b/JavaScript/a-bytes.js @@ -1,7 +1,7 @@ 'use strict'; -const memoizeBytes = (fn, size) => { - let cache = {}; +const memoizeBytes = (fn, size = '1K', logging = true) => { + let cache = new Map(); const generateKey = (...args) => args.join('|'); @@ -24,57 +24,53 @@ const memoizeBytes = (fn, size) => { const getSize = (obj) => { let bytes = 0; - for (const i in obj) { - switch (typeof obj[i]) { - case 'number': - bytes += 8; - break; - case 'boolean': - bytes += 4; - break; - case 'string': - bytes += obj[i].length * 2; - break; - case 'object': - bytes += getSize(obj[i]); - break; - default: throw new Error('getSize: Wrong field type.'); - } - } + const dictTypes = { + number: () => 8, + boolean: () => 4, + string: (str) => str.length * 2, + object: (ob) => getSize(ob), + }; + obj.forEach((value) => { + const getBytes = dictTypes[typeof value]; + bytes += getBytes(value); + }); return bytes; }; return (...args) => { const key = generateKey(...args); + const record = cache.get(key); - if (cache[key]) { - console.log(`From cache: ${key}`); + if (record) { + if (logging) console.log(`From cache: ${key}`); return cache[key]; } - if (getSize(cache) >= sizeInBytes) { - console.log(`Cache with size ${getSize(cache)} bytes cleaned`); - cache = {}; + const sizeOfCache = getSize(cache); + if (sizeOfCache >= sizeInBytes) { + console.log(`Cache with size ${sizeOfCache} bytes cleaned`); + cache = new Map(); } - console.log(`Calculating: ${key}`); - cache[key] = fn(...args); - console.dir(`Cache size: ${getSize(cache)}`); - return fn(...args); + if (logging) console.log(`Calculating: ${key}`); + const res = fn(...args); + cache.set(key, res); + if (logging) console.dir(`Cache size: ${sizeOfCache}`); + return res; }; }; //USAGE const sum = (a, b) => a + b; -const sumB = memoizeBytes(sum, '32B'); -console.log(`sumB(1, 2) = ${sumB(1, 2)}`); -console.log(`sumB(1, 2) = ${sumB(1, 2)}`); -console.log(`sumB(2, 3) = ${sumB(2, 3)}`); -console.log(`sumB(3, 2) = ${sumB(3, 2)}`); -console.log(`sumB(4, 2) = ${sumB(4, 2)}`); -console.log(`sumB(2, 3) = ${sumB(2, 3)}`); -console.log(`sumB(1, 3) = ${sumB(1, 3)}`); -console.log(`sumB(4, 3) = ${sumB(4, 3)}`); -console.log(`sumB(9, 3) = ${sumB(1, 3)}`); -console.log(`sumB(8, 3) = ${sumB(8, 3)}`); +const sum1 = memoizeBytes(sum, '32B'); +console.log(`sum1(1, 2) = ${sum1(1, 2)}`); +console.log(`sum1(1, 2) = ${sum1(1, 2)}`); +console.log(`sum1(2, 3) = ${sum1(2, 3)}`); +console.log(`sum1(3, 2) = ${sum1(3, 2)}`); +console.log(`sum1(4, 2) = ${sum1(4, 2)}`); +console.log(`sum1(2, 3) = ${sum1(2, 3)}`); +console.log(`sum1(1, 3) = ${sum1(1, 3)}`); + +const sum2 = memoizeBytes(sum, '0.5K', false); +for (let i = 0; i < 100; i++, sum2(i, i - 1));