diff --git a/JavaScript/8-timeout.js b/JavaScript/8-timeout.js new file mode 100644 index 0000000..aa89832 --- /dev/null +++ b/JavaScript/8-timeout.js @@ -0,0 +1,77 @@ +'use strict'; + +const memoize = (fn, msec = 100) => { + const cache = {}; + let timer = null; + const hasKey = (key) => Object.keys(cache).includes(key); + const generateKey = (args) => { + let key = ''; + for (const arg of args) + key += `${arg}~${typeof arg}|`; + return key; + }; + + 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].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] = { value: res, lastUse: new Date() }; + console.log(cache); + return res; + }; + + return func; +}; + +const sum = (a, b) => a + b; + +const sumM = memoize(sum, 250); + +//USAGE +sumM(1, -1); +sumM(2, -1); +setTimeout(() => { + sumM(1, -1); + setTimeout(() => sumM(2, -1), 100); + setTimeout(() => { + sumM(2, 3); + setTimeout(() => sumM(3, 4), 1000); + }, 500); +}, 200); 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..8b25c0e --- /dev/null +++ b/JavaScript/a-bytes.js @@ -0,0 +1,76 @@ +'use strict'; + +const memoizeBytes = (fn, size = '1K', logging = true) => { + let cache = new Map(); + + 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; + 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 (record) { + if (logging) console.log(`From cache: ${key}`); + return cache[key]; + } + + const sizeOfCache = getSize(cache); + if (sizeOfCache >= sizeInBytes) { + console.log(`Cache with size ${sizeOfCache} bytes cleaned`); + cache = new Map(); + } + + 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 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)); 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)`