nano-memoize
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -184,3 +184,3 @@ 'use strict'; | ||
}) | ||
.add('namo-memoize', () => { | ||
.add('nano-memoize', () => { | ||
mNano(fibonacciNumber); | ||
@@ -259,3 +259,3 @@ }) | ||
}) | ||
.add('namo-memoize', () => { | ||
.add('nano-memoize', () => { | ||
mNano(fibonacciNumber); | ||
@@ -262,0 +262,0 @@ }) |
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
(function() { | ||
"use strict"; | ||
const hasVargs = (f) => { | ||
const hsVrgs = (f) => { | ||
const s = f+"", | ||
@@ -10,12 +10,12 @@ i = s.indexOf("..."); | ||
function nanomemoize (fn, options={}) { | ||
// for single argument functions, just use a JS object key look-up | ||
function single (f,s,change,serializer,arg) { | ||
// for sngl argument functions, just use a JS object key look-up | ||
function sngl (f,s,chng,serializer,arg) { | ||
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg)); | ||
if(change) change(key); | ||
if(chng) chng(key); | ||
return s[key] || ( s[key] = f.call(this, arg)); | ||
} | ||
// for multiple arg functions, loop through a cache of all the args | ||
// for mltpl arg functions, loop through a cache of all the args | ||
// looking at each arg separately so a test can abort as soon as possible | ||
function multiple(f,k,v,eq,change,max=0,...args) { | ||
function mltpl(f,k,v,eq,chng,max=0,...args) { | ||
const rslt = {}; | ||
@@ -37,3 +37,3 @@ for(let i=0;i<k.length;i++) { // an array of arrays of args | ||
const i = rslt.i>=0 ? rslt.i : v.length; | ||
if(change) { change(i); } | ||
if(chng) { chng(i); } | ||
return typeof rslt.v === "undefined" ? v[i] = f.call(this,...(k[i] = args)) : rslt.v; | ||
@@ -46,38 +46,33 @@ } | ||
maxArgs, | ||
vargs = hasVargs(fn) | ||
vargs = hsVrgs(fn), | ||
expireInterval = 1 | ||
} = options, | ||
s = {}, // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
v = [], // multiple arg function result cache | ||
c = {}, // key change cache | ||
change = (cache,key) => { // logs key changes | ||
s = {}, // sngl arg function key/value cache | ||
k = [], // mltpl arg function arg key cache | ||
v = [], // mltpl arg function result cache | ||
c = {}, // key chng cache | ||
chng = (cache,key) => { // logs key chngs | ||
c[key] = {key,cache}; | ||
}, | ||
t = {}, | ||
timeout = (change) => { // deletes timed-out keys | ||
if(t[change.key]) { clearTimeout(t[change.key]); } | ||
t[change.key] = setTimeout(() => { | ||
delete change.cache[change.key]; | ||
delete t[change.key]; | ||
tmout = (chng) => { // deletes timed-out keys | ||
if(t[chng.key]) { clearTimeout(t[chng.key]); } | ||
t[chng.key] = setTimeout(() => { | ||
delete chng.cache[chng.key]; | ||
delete t[chng.key]; | ||
},maxAge); | ||
}; | ||
setInterval(() => { // process key changes out of cycle for speed | ||
for(let p in c) { | ||
if(maxAge) { timeout(c[p]); } | ||
delete c[p]; | ||
} | ||
},1); | ||
let f, | ||
unary = fn.length===1 && !equals && !vargs; | ||
unry = fn.length===1 && !equals && !vargs; | ||
// pre-bind core arguments, faster than using a closure or passing on stack or in this case using a partial | ||
if(unary) { | ||
f = single.bind( | ||
if(unry) { | ||
f = sngl.bind( | ||
this, | ||
fn, | ||
s, | ||
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s | ||
(maxAge ? chng.bind(this,s): null), // turn chng logging on and bind to arg cache s | ||
serializer | ||
); | ||
} else { | ||
f = multiple.bind( | ||
f = mltpl.bind( | ||
this, | ||
@@ -88,7 +83,7 @@ fn, | ||
equals || ((a,b) => a===b), // default to just a regular strict comparison | ||
(maxAge ? change.bind(this,v): null), // turn change logging on and bind to arg cache v | ||
(maxAge ? chng.bind(this,v): null), // turn chng logging on and bind to arg cache v | ||
maxArgs | ||
); | ||
} | ||
// reset all the caches, must change array length or delete keys on objects to retain bind integrity | ||
// reset all the caches, must chng array length or delete keys on objects to retain bind integrity | ||
f.clear = () => { | ||
@@ -101,5 +96,13 @@ Object.keys(s).forEach((k) => delete s[k]); | ||
}; | ||
f.keys = () => (!unary ? k.slice() : null); | ||
f.values = () => (!unary ? v.slice() : null); | ||
f.keyValues = () => (unary ? Object.assign({},s) : null); | ||
f.keys = () => (!unry ? k.slice() : null); | ||
f.values = () => (!unry ? v.slice() : null); | ||
f.keyValues = () => (unry ? Object.assign({},s) : null); | ||
if(expireInterval) { | ||
f.interval = setInterval(() => { // process key chngs out of cycle for speed | ||
for(const p in c) { | ||
if(maxAge) { tmout(c[p]); } | ||
delete c[p]; | ||
} | ||
},expireInterval); | ||
} | ||
return f; | ||
@@ -106,0 +109,0 @@ } |
@@ -1,1 +0,1 @@ | ||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){(function(){"use strict";const hasVargs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)};function nanomemoize(fn,options={}){function single(f,s,change,serializer,arg){const key=!arg||typeof arg==="number"||typeof arg==="boolean"?arg:serializer(arg);if(change)change(key);return s[key]||(s[key]=f.call(this,arg))}function multiple(f,k,v,eq,change,max=0,...args){const rslt={};for(let i=0;i<k.length;i++){let key=k[i];if(max){key=key.slice(0,max)}if(key.length===args.length||max&&key.length<args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!eq(key[j],args[j])){break}if(j===max){rslt.i=i;rslt.v=v[i]}}}}const i=rslt.i>=0?rslt.i:v.length;if(change){change(i)}return typeof rslt.v==="undefined"?v[i]=f.call(this,...k[i]=args):rslt.v}const{serializer:serializer=(value=>JSON.stringify(value)),equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=hasVargs(fn)}=options,s={},k=[],v=[],c={},change=(cache,key)=>{c[key]={key:key,cache:cache}},t={},timeout=change=>{if(t[change.key]){clearTimeout(t[change.key])}t[change.key]=setTimeout(()=>{delete change.cache[change.key];delete t[change.key]},maxAge)};setInterval(()=>{for(let p in c){if(maxAge){timeout(c[p])}delete c[p]}},1);let f,unary=fn.length===1&&!equals&&!vargs;if(unary){f=single.bind(this,fn,s,maxAge?change.bind(this,s):null,serializer)}else{f=multiple.bind(this,fn,k,v,equals||((a,b)=>a===b),maxAge?change.bind(this,v):null,maxArgs)}f.clear=(()=>{Object.keys(s).forEach(k=>delete s[k]);k.length=0;v.length=0;Object.keys(c).forEach(k=>delete c[k]);Object.keys(t).forEach(k=>{clearTimeout(t[k]);delete t[k]})});f.keys=(()=>!unary?k.slice():null);f.values=(()=>!unary?v.slice():null);f.keyValues=(()=>unary?Object.assign({},s):null);return f}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this)},{}]},{},[1]); | ||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){(function(){"use strict";const hsVrgs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)};function nanomemoize(fn,options={}){function sngl(f,s,chng,serializer,arg){const key=!arg||typeof arg==="number"||typeof arg==="boolean"?arg:serializer(arg);if(chng)chng(key);return s[key]||(s[key]=f.call(this,arg))}function mltpl(f,k,v,eq,chng,max=0,...args){const rslt={};for(let i=0;i<k.length;i++){let key=k[i];if(max){key=key.slice(0,max)}if(key.length===args.length||max&&key.length<args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!eq(key[j],args[j])){break}if(j===max){rslt.i=i;rslt.v=v[i]}}}}const i=rslt.i>=0?rslt.i:v.length;if(chng){chng(i)}return typeof rslt.v==="undefined"?v[i]=f.call(this,...k[i]=args):rslt.v}const{serializer:serializer=(value=>JSON.stringify(value)),equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=hsVrgs(fn),expireInterval:expireInterval=1}=options,s={},k=[],v=[],c={},chng=(cache,key)=>{c[key]={key:key,cache:cache}},t={},tmout=chng=>{if(t[chng.key]){clearTimeout(t[chng.key])}t[chng.key]=setTimeout(()=>{delete chng.cache[chng.key];delete t[chng.key]},maxAge)};let f,unry=fn.length===1&&!equals&&!vargs;if(unry){f=sngl.bind(this,fn,s,maxAge?chng.bind(this,s):null,serializer)}else{f=mltpl.bind(this,fn,k,v,equals||((a,b)=>a===b),maxAge?chng.bind(this,v):null,maxArgs)}f.clear=(()=>{Object.keys(s).forEach(k=>delete s[k]);k.length=0;v.length=0;Object.keys(c).forEach(k=>delete c[k]);Object.keys(t).forEach(k=>{clearTimeout(t[k]);delete t[k]})});f.keys=(()=>!unry?k.slice():null);f.values=(()=>!unry?v.slice():null);f.keyValues=(()=>unry?Object.assign({},s):null);if(expireInterval){f.interval=setInterval(()=>{for(const p in c){if(maxAge){tmout(c[p])}delete c[p]}},expireInterval)}return f}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this)},{}]},{},[1]); |
(function() { | ||
"use strict"; | ||
const hasVargs = (f) => { | ||
const hsVrgs = (f) => { | ||
const s = f+"", | ||
@@ -9,12 +9,12 @@ i = s.indexOf("..."); | ||
function nanomemoize (fn, options={}) { | ||
// for single argument functions, just use a JS object key look-up | ||
function single (f,s,change,serializer,arg) { | ||
// for sngl argument functions, just use a JS object key look-up | ||
function sngl (f,s,chng,serializer,arg) { | ||
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg)); | ||
if(change) change(key); | ||
if(chng) chng(key); | ||
return s[key] || ( s[key] = f.call(this, arg)); | ||
} | ||
// for multiple arg functions, loop through a cache of all the args | ||
// for mltpl arg functions, loop through a cache of all the args | ||
// looking at each arg separately so a test can abort as soon as possible | ||
function multiple(f,k,v,eq,change,max=0,...args) { | ||
function mltpl(f,k,v,eq,chng,max=0,...args) { | ||
const rslt = {}; | ||
@@ -36,3 +36,3 @@ for(let i=0;i<k.length;i++) { // an array of arrays of args | ||
const i = rslt.i>=0 ? rslt.i : v.length; | ||
if(change) { change(i); } | ||
if(chng) { chng(i); } | ||
return typeof rslt.v === "undefined" ? v[i] = f.call(this,...(k[i] = args)) : rslt.v; | ||
@@ -45,38 +45,33 @@ } | ||
maxArgs, | ||
vargs = hasVargs(fn) | ||
vargs = hsVrgs(fn), | ||
expireInterval = 1 | ||
} = options, | ||
s = {}, // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
v = [], // multiple arg function result cache | ||
c = {}, // key change cache | ||
change = (cache,key) => { // logs key changes | ||
s = {}, // sngl arg function key/value cache | ||
k = [], // mltpl arg function arg key cache | ||
v = [], // mltpl arg function result cache | ||
c = {}, // key chng cache | ||
chng = (cache,key) => { // logs key chngs | ||
c[key] = {key,cache}; | ||
}, | ||
t = {}, | ||
timeout = (change) => { // deletes timed-out keys | ||
if(t[change.key]) { clearTimeout(t[change.key]); } | ||
t[change.key] = setTimeout(() => { | ||
delete change.cache[change.key]; | ||
delete t[change.key]; | ||
tmout = (chng) => { // deletes timed-out keys | ||
if(t[chng.key]) { clearTimeout(t[chng.key]); } | ||
t[chng.key] = setTimeout(() => { | ||
delete chng.cache[chng.key]; | ||
delete t[chng.key]; | ||
},maxAge); | ||
}; | ||
setInterval(() => { // process key changes out of cycle for speed | ||
for(let p in c) { | ||
if(maxAge) { timeout(c[p]); } | ||
delete c[p]; | ||
} | ||
},1); | ||
let f, | ||
unary = fn.length===1 && !equals && !vargs; | ||
unry = fn.length===1 && !equals && !vargs; | ||
// pre-bind core arguments, faster than using a closure or passing on stack or in this case using a partial | ||
if(unary) { | ||
f = single.bind( | ||
if(unry) { | ||
f = sngl.bind( | ||
this, | ||
fn, | ||
s, | ||
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s | ||
(maxAge ? chng.bind(this,s): null), // turn chng logging on and bind to arg cache s | ||
serializer | ||
); | ||
} else { | ||
f = multiple.bind( | ||
f = mltpl.bind( | ||
this, | ||
@@ -87,7 +82,7 @@ fn, | ||
equals || ((a,b) => a===b), // default to just a regular strict comparison | ||
(maxAge ? change.bind(this,v): null), // turn change logging on and bind to arg cache v | ||
(maxAge ? chng.bind(this,v): null), // turn chng logging on and bind to arg cache v | ||
maxArgs | ||
); | ||
} | ||
// reset all the caches, must change array length or delete keys on objects to retain bind integrity | ||
// reset all the caches, must chng array length or delete keys on objects to retain bind integrity | ||
f.clear = () => { | ||
@@ -100,5 +95,13 @@ Object.keys(s).forEach((k) => delete s[k]); | ||
}; | ||
f.keys = () => (!unary ? k.slice() : null); | ||
f.values = () => (!unary ? v.slice() : null); | ||
f.keyValues = () => (unary ? Object.assign({},s) : null); | ||
f.keys = () => (!unry ? k.slice() : null); | ||
f.values = () => (!unry ? v.slice() : null); | ||
f.keyValues = () => (unry ? Object.assign({},s) : null); | ||
if(expireInterval) { | ||
f.interval = setInterval(() => { // process key chngs out of cycle for speed | ||
for(const p in c) { | ||
if(maxAge) { tmout(c[p]); } | ||
delete c[p]; | ||
} | ||
},expireInterval); | ||
} | ||
return f; | ||
@@ -105,0 +108,0 @@ } |
@@ -1,1 +0,1 @@ | ||
(function(){"use strict";const hasVargs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)};function nanomemoize(fn,options={}){function single(f,s,change,serializer,arg){const key=!arg||typeof arg==="number"||typeof arg==="boolean"?arg:serializer(arg);if(change)change(key);return s[key]||(s[key]=f.call(this,arg))}function multiple(f,k,v,eq,change,max=0,...args){const rslt={};for(let i=0;i<k.length;i++){let key=k[i];if(max){key=key.slice(0,max)}if(key.length===args.length||max&&key.length<args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!eq(key[j],args[j])){break}if(j===max){rslt.i=i;rslt.v=v[i]}}}}const i=rslt.i>=0?rslt.i:v.length;if(change){change(i)}return typeof rslt.v==="undefined"?v[i]=f.call(this,...k[i]=args):rslt.v}const{serializer:serializer=(value=>JSON.stringify(value)),equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=hasVargs(fn)}=options,s={},k=[],v=[],c={},change=(cache,key)=>{c[key]={key:key,cache:cache}},t={},timeout=change=>{if(t[change.key]){clearTimeout(t[change.key])}t[change.key]=setTimeout(()=>{delete change.cache[change.key];delete t[change.key]},maxAge)};setInterval(()=>{for(let p in c){if(maxAge){timeout(c[p])}delete c[p]}},1);let f,unary=fn.length===1&&!equals&&!vargs;if(unary){f=single.bind(this,fn,s,maxAge?change.bind(this,s):null,serializer)}else{f=multiple.bind(this,fn,k,v,equals||((a,b)=>a===b),maxAge?change.bind(this,v):null,maxArgs)}f.clear=(()=>{Object.keys(s).forEach(k=>delete s[k]);k.length=0;v.length=0;Object.keys(c).forEach(k=>delete c[k]);Object.keys(t).forEach(k=>{clearTimeout(t[k]);delete t[k]})});f.keys=(()=>!unary?k.slice():null);f.values=(()=>!unary?v.slice():null);f.keyValues=(()=>unary?Object.assign({},s):null);return f}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this); | ||
(function(){"use strict";const hsVrgs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)};function nanomemoize(fn,options={}){function sngl(f,s,chng,serializer,arg){const key=!arg||typeof arg==="number"||typeof arg==="boolean"?arg:serializer(arg);if(chng)chng(key);return s[key]||(s[key]=f.call(this,arg))}function mltpl(f,k,v,eq,chng,max=0,...args){const rslt={};for(let i=0;i<k.length;i++){let key=k[i];if(max){key=key.slice(0,max)}if(key.length===args.length||max&&key.length<args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!eq(key[j],args[j])){break}if(j===max){rslt.i=i;rslt.v=v[i]}}}}const i=rslt.i>=0?rslt.i:v.length;if(chng){chng(i)}return typeof rslt.v==="undefined"?v[i]=f.call(this,...k[i]=args):rslt.v}const{serializer:serializer=(value=>JSON.stringify(value)),equals:equals,maxAge:maxAge,maxArgs:maxArgs,vargs:vargs=hsVrgs(fn),expireInterval:expireInterval=1}=options,s={},k=[],v=[],c={},chng=(cache,key)=>{c[key]={key:key,cache:cache}},t={},tmout=chng=>{if(t[chng.key]){clearTimeout(t[chng.key])}t[chng.key]=setTimeout(()=>{delete chng.cache[chng.key];delete t[chng.key]},maxAge)};let f,unry=fn.length===1&&!equals&&!vargs;if(unry){f=sngl.bind(this,fn,s,maxAge?chng.bind(this,s):null,serializer)}else{f=mltpl.bind(this,fn,k,v,equals||((a,b)=>a===b),maxAge?chng.bind(this,v):null,maxArgs)}f.clear=(()=>{Object.keys(s).forEach(k=>delete s[k]);k.length=0;v.length=0;Object.keys(c).forEach(k=>delete c[k]);Object.keys(t).forEach(k=>{clearTimeout(t[k]);delete t[k]})});f.keys=(()=>!unry?k.slice():null);f.values=(()=>!unry?v.slice():null);f.keyValues=(()=>unry?Object.assign({},s):null);if(expireInterval){f.interval=setInterval(()=>{for(const p in c){if(maxAge){tmout(c[p])}delete c[p]}},expireInterval)}return f}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this); |
71
index.js
(function() { | ||
"use strict"; | ||
const hasVargs = (f) => { | ||
const hsVrgs = (f) => { | ||
const s = f+"", | ||
@@ -9,12 +9,12 @@ i = s.indexOf("..."); | ||
function nanomemoize (fn, options={}) { | ||
// for single argument functions, just use a JS object key look-up | ||
function single (f,s,change,serializer,arg) { | ||
// for sngl argument functions, just use a JS object key look-up | ||
function sngl (f,s,chng,serializer,arg) { | ||
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg)); | ||
if(change) change(key); | ||
if(chng) chng(key); | ||
return s[key] || ( s[key] = f.call(this, arg)); | ||
} | ||
// for multiple arg functions, loop through a cache of all the args | ||
// for mltpl arg functions, loop through a cache of all the args | ||
// looking at each arg separately so a test can abort as soon as possible | ||
function multiple(f,k,v,eq,change,max=0,...args) { | ||
function mltpl(f,k,v,eq,chng,max=0,...args) { | ||
const rslt = {}; | ||
@@ -36,3 +36,3 @@ for(let i=0;i<k.length;i++) { // an array of arrays of args | ||
const i = rslt.i>=0 ? rslt.i : v.length; | ||
if(change) { change(i); } | ||
if(chng) { chng(i); } | ||
return typeof rslt.v === "undefined" ? v[i] = f.call(this,...(k[i] = args)) : rslt.v; | ||
@@ -45,38 +45,33 @@ } | ||
maxArgs, | ||
vargs = hasVargs(fn) | ||
vargs = hsVrgs(fn), | ||
expireInterval = 1 | ||
} = options, | ||
s = {}, // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
v = [], // multiple arg function result cache | ||
c = {}, // key change cache | ||
change = (cache,key) => { // logs key changes | ||
s = {}, // sngl arg function key/value cache | ||
k = [], // mltpl arg function arg key cache | ||
v = [], // mltpl arg function result cache | ||
c = {}, // key chng cache | ||
chng = (cache,key) => { // logs key chngs | ||
c[key] = {key,cache}; | ||
}, | ||
t = {}, | ||
timeout = (change) => { // deletes timed-out keys | ||
if(t[change.key]) { clearTimeout(t[change.key]); } | ||
t[change.key] = setTimeout(() => { | ||
delete change.cache[change.key]; | ||
delete t[change.key]; | ||
tmout = (chng) => { // deletes timed-out keys | ||
if(t[chng.key]) { clearTimeout(t[chng.key]); } | ||
t[chng.key] = setTimeout(() => { | ||
delete chng.cache[chng.key]; | ||
delete t[chng.key]; | ||
},maxAge); | ||
}; | ||
setInterval(() => { // process key changes out of cycle for speed | ||
for(let p in c) { | ||
if(maxAge) { timeout(c[p]); } | ||
delete c[p]; | ||
} | ||
},1); | ||
let f, | ||
unary = fn.length===1 && !equals && !vargs; | ||
unry = fn.length===1 && !equals && !vargs; | ||
// pre-bind core arguments, faster than using a closure or passing on stack or in this case using a partial | ||
if(unary) { | ||
f = single.bind( | ||
if(unry) { | ||
f = sngl.bind( | ||
this, | ||
fn, | ||
s, | ||
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s | ||
(maxAge ? chng.bind(this,s): null), // turn chng logging on and bind to arg cache s | ||
serializer | ||
); | ||
} else { | ||
f = multiple.bind( | ||
f = mltpl.bind( | ||
this, | ||
@@ -87,7 +82,7 @@ fn, | ||
equals || ((a,b) => a===b), // default to just a regular strict comparison | ||
(maxAge ? change.bind(this,v): null), // turn change logging on and bind to arg cache v | ||
(maxAge ? chng.bind(this,v): null), // turn chng logging on and bind to arg cache v | ||
maxArgs | ||
); | ||
} | ||
// reset all the caches, must change array length or delete keys on objects to retain bind integrity | ||
// reset all the caches, must chng array length or delete keys on objects to retain bind integrity | ||
f.clear = () => { | ||
@@ -100,5 +95,13 @@ Object.keys(s).forEach((k) => delete s[k]); | ||
}; | ||
f.keys = () => (!unary ? k.slice() : null); | ||
f.values = () => (!unary ? v.slice() : null); | ||
f.keyValues = () => (unary ? Object.assign({},s) : null); | ||
f.keys = () => (!unry ? k.slice() : null); | ||
f.values = () => (!unry ? v.slice() : null); | ||
f.keyValues = () => (unry ? Object.assign({},s) : null); | ||
if(expireInterval) { | ||
f.interval = setInterval(() => { // process key chngs out of cycle for speed | ||
for(const p in c) { | ||
if(maxAge) { tmout(c[p]); } | ||
delete c[p]; | ||
} | ||
},expireInterval); | ||
} | ||
return f; | ||
@@ -105,0 +108,0 @@ } |
{ | ||
"name": "nano-memoize", | ||
"version": "v1.0.0", | ||
"version": "v1.0.1", | ||
"description": "Faster than fast, smaller than micro ... a nano speed and nano size memoizer.", | ||
@@ -5,0 +5,0 @@ "engines": {}, |
193
README.md
@@ -10,23 +10,44 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/30ce201484754fa5b0a6c6046abb842d)](https://www.codacy.com/app/syblackwell/nano-memoize?utm_source=github.com&utm_medium=referral&utm_content=anywhichway/nano-memoize&utm_campaign=Badge_Grade) | ||
The speed tests are below. `nano-memoize` is the fastest in all cases. For single argument functions is it comparable to, but slightly and probably un-importantly faster than, `fast-memoize`. For multiple argument functions it is comparable to, but slightly and probably un-importantly faster than, `micro-memoize`. | ||
The speed tests are below. In most cases `nano-memoize` is the fastest. | ||
* For single primitive argument functions it is comparable to, but slightly and probably un-importantly faster that `fast-memoize`. | ||
* For single object argument functions it is always by far the fastest. | ||
* For multiple primitive argument functions functions`nano-memoize` slightly and probably un-importantly faster than `fast-memoize`. | ||
We have found that benchmarks can vary dramatically from O/S to O/S or Node version to Node version. These tests were run on a Windows 10 64bit 2.4ghz machine with 8GB RAM and Node v9.4.0. | ||
* For multiple object argument functions `fast-memoize` slightly and probably un-importantly faster. | ||
We have found that benchmarks can vary dramatically from O/S to O/S or Node version to Node version. These tests were run on a Windows 10 64bit 2.4ghz machine with 8GB RAM and Node v9.4.0. Also, even with multiple samplings, garbage collection can have a substative impact and multiple runs in different orders are really required for apples-to-apples comparisons. | ||
Functions with a single primitive parameter... | ||
| Name | Ops / sec | Relative margin of error | Sample size | | ||
|---------------|-------------|--------------------------|-------------| | ||
| nano-memoize | 152,526,010 | � 2.58% | 80 | | ||
| fast-memoize | 147,683,192 | � 2.90% | 85 | | ||
| micro-memoize | 22,682,348 | � 3.69% | 75 | | ||
| iMemoized | 22,292,411 | � 4.47% | 72 | | ||
| lodash | 20,937,311 | � 1.94% | 88 | | ||
| moize | 16,296,876 | � 4.77% | 74 | | ||
| memoizee | 9,651,118 | � 3.07% | 86 | | ||
| underscore | 9,266,277 | � 2.66% | 75 | | ||
| lru-memoize | 6,676,849 | � 2.93% | 87 | | ||
| addy-osmani | 3,899,834 | � 2.27% | 86 | | ||
| memoizerific | 3,753,347 | � 2.33% | 86 | | ||
| ramda | 493,665 | � 1.77% | 88 | | ||
+----------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+---------------+-------------+--------------------------+-------------� | ||
� namo-memoize � 277,174,954 � � 0.39% � 94 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� fast-memoize � 243,829,313 � � 4.97% � 81 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� iMemoized � 49,406,719 � � 3.90% � 82 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� micro-memoize � 48,245,239 � � 2.19% � 89 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� moize � 47,380,879 � � 0.59% � 88 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� lru-memoize � 39,284,232 � � 4.35% � 87 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� lodash � 31,464,058 � � 2.91% � 91 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� memoizee � 19,406,111 � � 4.90% � 79 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� underscore � 16,986,840 � � 5.83% � 75 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� addy-osmani � 4,496,619 � � 0.98% � 92 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� memoizerific � 2,394,952 � � 6.96% � 49 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� ramda � 1,095,063 � � 2.10% � 86 � | ||
+----------------------------------------------------------------------+ | ||
@@ -36,16 +57,21 @@ | ||
| Name | Ops / sec | Relative margin of error | Sample size | | ||
|---------------|------------|--------------------------|-------------| | ||
| nano-memoize | 53,741,011 | � 2.06% | 85 | | ||
| fast-memoize | 51,041,370 | � 2.40% | 82 | | ||
| micro-memoize | 22,638,078 | � 3.96% | 77 | | ||
| lodash | 22,187,376 | � 1.72% | 83 | | ||
| moize | 19,446,817 | � 3.32% | 81 | | ||
| underscore | 13,643,959 | � 3.17% | 81 | | ||
| iMemoized | 11,926,976 | � 5.90% | 80 | | ||
| memoizee | 8,010,016 | � 1.99% | 83 | | ||
| lru-memoize | 5,709,156 | � 1.89% | 89 | | ||
| memoizerific | 3,817,781 | � 1.46% | 90 | | ||
| addy-osmani | 3,699,956 | � 3.30% | 85 | | ||
| ramda | 793,756 | � 1.92% | 87 | | ||
+----------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+---------------+-------------+--------------------------+-------------� | ||
� namo-memoize � 271,647,146 � 0.74% � 90 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� micro-memoize � 44,126,430 � 4.22% � 81 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� fast-memoize � 44,125,722 � 2.14% � 82 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� iMemoized � 43,981,304 � 1.61% � 89 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� moize � 32,603,505 � 3.19% � 14 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� lodash � 31,277,037 � 1.16% � 88 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� underscore � 20,293,644 � 1.02% � 88 � | ||
+---------------+-------------+--------------------------+-------------� | ||
� memoizee � 11,533,134 � 1.35% � 89 � | ||
+---------------+-------------+--------------------------+-------------� | ||
@@ -55,13 +81,23 @@ | ||
| Name | Ops / sec | Relative margin of error | Sample size | | ||
|---------------|------------|--------------------------|-------------| | ||
| nano-memoize | 18,408,074 | � 1.24% | 85 | | ||
| micro-memoize | 17,310,593 | � 1.11% | 85 | | ||
| moize | 14,457,697 | � 0.79% | 90 | | ||
| memoizee | 7,723,320 | � 0.54% | 94 | | ||
| iMemoized | 5,934,041 | � 1.03% | 90 | | ||
| lru-memoize | 5,388,273 | � 0.52% | 94 | | ||
| memoizerific | 3,206,479 | � 0.30% | 93 | | ||
| addy-osmani | 2,397,744 | � 0.48% | 93 | | ||
| fast-memoize | 899,483 | � 0.37% | 91 | | ||
+---------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+---------------+------------+--------------------------+-------------� | ||
� nano-memoize � 24,739,433 � 0.98% � 85 � | ||
+---------------+------------+--------------------------+-------------� | ||
� micro-memoize � 23,131,341 � 3.33% � 74 � | ||
+---------------+------------+--------------------------+-------------� | ||
� moize � 20,241,359 � 2.45% � 81 � | ||
+---------------+------------+--------------------------+-------------� | ||
� memoizee � 9,917,821 � 2.58% � 85 � | ||
+---------------+------------+--------------------------+-------------� | ||
� lru-memoize � 7,582,999 � 2.85% � 82 � | ||
+---------------+------------+--------------------------+-------------� | ||
� iMemoized � 4,765,891 � 12.92% � 68 � | ||
+---------------+------------+--------------------------+-------------� | ||
� memoizerific � 3,200,253 � 3.02% � 84 � | ||
+---------------+------------+--------------------------+-------------� | ||
� addy-osmani � 2,240,692 � 2.28% � 87 � | ||
+---------------+------------+--------------------------+-------------� | ||
� fast-memoize � 885,271 � 3.99% � 82 � | ||
+---------------------------------------------------------------------+ | ||
@@ -71,12 +107,21 @@ | ||
| Name | Ops / sec | Relative margin of error | Sample size | | ||
|---------------|------------|--------------------------|-------------| | ||
| nano-memoize | 13,869,690 | � 1.25% | 86 | | ||
| micro-memoize | 13,192,239 | � 3.13% | 78 | | ||
| moize | 10,895,627 | � 3.33% | 71 | | ||
| memoizee | 5,794,981 | � 0.83% | 92 | | ||
| lru-memoize | 5,148,065 | � 0.44% | 92 | | ||
| memoizerific | 3,206,713 | � 0.86% | 93 | | ||
| addy-osmani | 996,705 | � 0.45% | 92 | | ||
| fast-memoize | 699,597 | � 1.17% | 91 | | ||
+---------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+---------------+------------+--------------------------+-------------� | ||
� micro-memoize � 23,846,343 � 3.35% � 84 � | ||
+---------------+------------+--------------------------+-------------� | ||
� nano-memoize � 17,861,879 � 2.49% � 84 � | ||
+---------------+------------+--------------------------+-------------� | ||
� moize � 17,147,054 � 5.62% � 63 � | ||
+---------------+------------+--------------------------+-------------� | ||
� lru-memoize � 7,247,819 � 3.85% � 81 � | ||
+---------------+------------+--------------------------+-------------� | ||
� memoizee � 6,860,227 � 1.17% � 88 � | ||
+---------------+------------+--------------------------+-------------� | ||
� memoizerific � 3,399,423 � 2.60% � 85 � | ||
+---------------+------------+--------------------------+-------------� | ||
� addy-osmani � 795,071 � 1.43% � 85 � | ||
+---------------+------------+--------------------------+-------------� | ||
� fast-memoize � 715,841 � 1.05% � 86 � | ||
+---------------------------------------------------------------------+ | ||
@@ -87,9 +132,15 @@ | ||
| Name | Ops / sec | Relative margin of error | Sample size | | ||
|---------------------------------------------------|------------|--------------------------|-------------| | ||
| nano-memoize deep equals (lodash isEqual) | 18,024,422 | � 1.78% | 83 | | ||
| micro-memoize deep equals (lodash isEqual) | 17,219,476 | � 0.69% | 86 | | ||
| nano-memoize deep equals (fast-equals deepEqual) | 14,732,731 | � 3.10% | 85 | | ||
| micro-memoize deep equals (fast-equals deepEqual) | 8,785,408 | � 11.28% | 51 | | ||
| micro-memoize deep equals (hash-it isEqual) | 5,744,080 | � 10.69% | 48 | | ||
+---------------------------------------------------------------------------------------------------------+ | ||
� Name � Ops / sec � Relative margin of error � Sample size � | ||
+---------------------------------------------------+------------+--------------------------+-------------� | ||
� micro-memoize deep equals (lodash isEqual) � 37,582,028 � 1.87% � 83 � | ||
+---------------------------------------------------+------------+--------------------------+-------------� | ||
� micro-memoize deep equals (fast-equals deepEqual) � 21,181,692 � 6.75% � 66 � | ||
+---------------------------------------------------+------------+--------------------------+-------------� | ||
� nanomemoize deep equals (fast-equals deepEqual) � 17,186,548 � 3.22% � 80 � | ||
+---------------------------------------------------+------------+--------------------------+-------------� | ||
� nanomemoize deep equals (lodash isEqual) � 14,995,992 � 3.49% � 75 � | ||
+---------------------------------------------------+------------+--------------------------+-------------� | ||
� micro-memoize deep equals (hash-it isEqual) � 14,376,860 � 3.27% � 78 � | ||
+---------------------------------------------------------------------------------------------------------+ | ||
@@ -106,5 +157,5 @@ | ||
npm install nano-memoize | ||
`npm install nano-memoize` | ||
use the code in the `browser` directory for the browser | ||
Use the code in the [`browser`](browser/) directory for the browser | ||
@@ -130,11 +181,21 @@ Since most devs are running a build pipeline, the code is not transpiled, although it is browserified | ||
{ | ||
maxArgs: number, // only use the provided maxArgs for cache look-up, useful for ignoring final callback arguments | ||
maxAge: number, // number of milliseconds to cache a result | ||
serializer: function, // the serializer/key generator to use for single argument functions (multi-argument functions do not use a serializer) | ||
equals: function, // the equals function to use for multi-argument functions, e.g. deepEquals for objects (single-argument functions use serializer not equals) | ||
vargs: boolean // forces the use of multi-argument paradigm, auto set if function has a spread argument or uses `arguments` in its body. | ||
// only use the provided maxArgs for cache look-up, useful for ignoring final callback arguments | ||
maxArgs: number, | ||
// number of milliseconds to cache a result | ||
maxAge: number, | ||
// the serializer/key generator to use for single argument functions (multi-argument functionsuse equals) | ||
serializer: function, | ||
// the equals function to use for multi-argument functions, e.g. deepEquals for objects (single-argument functions serializer) | ||
equals: function, | ||
// forces the use of multi-argument paradigm, auto set if function has a spread argument or uses `arguments` in its body. | ||
vargs: boolean | ||
// number of milliseconds between checks to expire memos, defaults to 1, set to 0 if you want to disable | ||
expireInterval: number, | ||
} | ||
``` | ||
# Release History (reverse chronological order) | ||
2019-02-16 v1.0.1 Memo expiration optimization. Issue 4 addressed. | ||
2018-04-13 v1.0.0 Code style improvements. | ||
@@ -160,2 +221,2 @@ | ||
2018=01-24 v0.0.1a ALPHA First public release. Benchmark code in repository not yet running. | ||
2018=01-24 v0.0.1a ALPHA First public release. Benchmark code in repository not yet running. |
(function() { | ||
"use strict"; | ||
const hasVargs = (f) => { | ||
const hsVrgs = (f) => { | ||
const s = f+"", | ||
@@ -9,12 +9,12 @@ i = s.indexOf("..."); | ||
function nanomemoize (fn, options={}) { | ||
// for single argument functions, just use a JS object key look-up | ||
function single (f,s,change,serializer,arg) { | ||
// for sngl argument functions, just use a JS object key look-up | ||
function sngl (f,s,chng,serializer,arg) { | ||
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1" | ||
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg)); | ||
if(change) change(key); | ||
if(chng) chng(key); | ||
return s[key] || ( s[key] = f.call(this, arg)); | ||
} | ||
// for multiple arg functions, loop through a cache of all the args | ||
// for mltpl arg functions, loop through a cache of all the args | ||
// looking at each arg separately so a test can abort as soon as possible | ||
function multiple(f,k,v,eq,change,max=0,...args) { | ||
function mltpl(f,k,v,eq,chng,max=0,...args) { | ||
const rslt = {}; | ||
@@ -36,3 +36,3 @@ for(let i=0;i<k.length;i++) { // an array of arrays of args | ||
const i = rslt.i>=0 ? rslt.i : v.length; | ||
if(change) { change(i); } | ||
if(chng) { chng(i); } | ||
return typeof rslt.v === "undefined" ? v[i] = f.call(this,...(k[i] = args)) : rslt.v; | ||
@@ -45,38 +45,33 @@ } | ||
maxArgs, | ||
vargs = hasVargs(fn) | ||
vargs = hsVrgs(fn), | ||
expireInterval = 1 | ||
} = options, | ||
s = {}, // single arg function key/value cache | ||
k = [], // multiple arg function arg key cache | ||
v = [], // multiple arg function result cache | ||
c = {}, // key change cache | ||
change = (cache,key) => { // logs key changes | ||
s = {}, // sngl arg function key/value cache | ||
k = [], // mltpl arg function arg key cache | ||
v = [], // mltpl arg function result cache | ||
c = {}, // key chng cache | ||
chng = (cache,key) => { // logs key chngs | ||
c[key] = {key,cache}; | ||
}, | ||
t = {}, | ||
timeout = (change) => { // deletes timed-out keys | ||
if(t[change.key]) { clearTimeout(t[change.key]); } | ||
t[change.key] = setTimeout(() => { | ||
delete change.cache[change.key]; | ||
delete t[change.key]; | ||
tmout = (chng) => { // deletes timed-out keys | ||
if(t[chng.key]) { clearTimeout(t[chng.key]); } | ||
t[chng.key] = setTimeout(() => { | ||
delete chng.cache[chng.key]; | ||
delete t[chng.key]; | ||
},maxAge); | ||
}; | ||
setInterval(() => { // process key changes out of cycle for speed | ||
for(let p in c) { | ||
if(maxAge) { timeout(c[p]); } | ||
delete c[p]; | ||
} | ||
},1); | ||
let f, | ||
unary = fn.length===1 && !equals && !vargs; | ||
unry = fn.length===1 && !equals && !vargs; | ||
// pre-bind core arguments, faster than using a closure or passing on stack or in this case using a partial | ||
if(unary) { | ||
f = single.bind( | ||
if(unry) { | ||
f = sngl.bind( | ||
this, | ||
fn, | ||
s, | ||
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s | ||
(maxAge ? chng.bind(this,s): null), // turn chng logging on and bind to arg cache s | ||
serializer | ||
); | ||
} else { | ||
f = multiple.bind( | ||
f = mltpl.bind( | ||
this, | ||
@@ -87,7 +82,7 @@ fn, | ||
equals || ((a,b) => a===b), // default to just a regular strict comparison | ||
(maxAge ? change.bind(this,v): null), // turn change logging on and bind to arg cache v | ||
(maxAge ? chng.bind(this,v): null), // turn chng logging on and bind to arg cache v | ||
maxArgs | ||
); | ||
} | ||
// reset all the caches, must change array length or delete keys on objects to retain bind integrity | ||
// reset all the caches, must chng array length or delete keys on objects to retain bind integrity | ||
f.clear = () => { | ||
@@ -100,5 +95,13 @@ Object.keys(s).forEach((k) => delete s[k]); | ||
}; | ||
f.keys = () => (!unary ? k.slice() : null); | ||
f.values = () => (!unary ? v.slice() : null); | ||
f.keyValues = () => (unary ? Object.assign({},s) : null); | ||
f.keys = () => (!unry ? k.slice() : null); | ||
f.values = () => (!unry ? v.slice() : null); | ||
f.keyValues = () => (unry ? Object.assign({},s) : null); | ||
if(expireInterval) { | ||
f.interval = setInterval(() => { // process key chngs out of cycle for speed | ||
for(const p in c) { | ||
if(maxAge) { tmout(c[p]); } | ||
delete c[p]; | ||
} | ||
},expireInterval); | ||
} | ||
return f; | ||
@@ -105,0 +108,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
52861
933
215