Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nano-memoize

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nano-memoize - npm Package Compare versions

Comparing version 0.0.1-a to 0.0.4-b

dist/nano-memoize.js

274

benchmark/index.js
'use strict';
/*MIT License
Core benchmark code copied from micro-memoize
Copyright (c) 2018 Tony Quetano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const Benchmark = require('benchmark');

@@ -16,132 +40,7 @@ const Table = require('cli-table2');

const moize = require('moize');
const microMemoize = require('../dist/micro-memoize.js').default; //require('../lib').default;
const microMemoize = require('micro-memoize').default; //require('../lib').default;
const iMemoized = require('iMemoized');
const nanomemoize = require('../src/nano-memoize.js');
/*function micromemo (fn, options={}) {
const {equals,maxAge,maxArgs,serializer} = options,
memoized = resolver.bind(this,fn,{},serializer,equals,maxAge);
return memoized;
}
function resolver (fn,singles,serializer=JSON.stringify,equals,maxAge,arg) {
if(!equals && arguments.length<=6) {
const cacheKey = typeof arg === 'string' || typeof arg === 'function' || typeof arg === 'object' ? serializer(arg) : arg;
return singles[cacheKey] || (singles[cacheKey]=fn.call(this, arg));
}
const args = [].slice.call(arguments,5),
result = {};
equals || (equals = (a,b) => a===b);
for(let i=0;i<keys.length;i++) {
if(keys[i]===null) { result.index = i; continue; }
const key = maxArgs ? keys[i].slice(0,maxArgs) : keys[i];
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break;
if(j===max) {
result.index = i;
result.value = values[i];
}
}
}
}
const i = result.index>=0 ? result.index : values.length;
if(maxAge) {
if(timeouts[i]) clearTimeout(timeouts[i]);
timeouts[i] = setTimeout(() => keys[i]=values[i]=timeouts[i]=null,maxAge)
}
return typeof(result.value)==="undefined" ? values[i] = fn.call(this,...(keys[i] = args)) : result.value;
};*/
function micromemo (fn, options={}) {
const {serializer=JSON.stringify,equals,maxAge,maxArgs,maxSize,stats} = options,
singles = {},
keys = [],
values = [],
changes = [],
change = (cache,key,property) => {
if(property) key = typeof(key) + "@" + key;
changes[key] = {key,cache};
},
hits = [],
hit = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
let record = hits[key];
if(!record) record = hits[key] = {count:0,cache};
hit.count++;
hit.time = Date.now();
},
timeouts = [],
timeout = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
if(timeouts[key]) clearTimeout(timeouts[key]);
timeouts[key] = setTimeout(() => cache[key]=timeouts[key]=null,maxAge);
};
let memoized;
if(fn.length===1 && !equals) {
memoized = single.bind(
this,
fn,
singles,
(maxSize || maxAge || stats ? change.bind(this,values): null),
serializer
);
} else {
memoized = multiple.bind(
this,
fn,
keys,
values,
serializer,
equals ? equals : (a,b) => a===b,
(maxSize || maxAge || stats ? change.bind(this,values): null),
maxArgs
);
}
memoized.clear = () => {
Object.keys(singles).forEach(key => delete singles[key]);
keys.splice(0,keys.length);
values.splice(0,values.length);
changes.splice(0,changes.length);
Object.keys(changes).forEach(key => key==="length" || delete changes[key]);
timeouts.forEach(timeout => !timeout || clearTimeout(timeout));
timeouts.splice(0,timeouts.length);
Object.keys(timeouts).forEach(key => key==="length" || delete timeouts[key]);
}
return memoized;
}
function single (f,cache,change,serializer,arg) {
if(arguments.length<=5) {
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg));
if(change) change(key,true);
return cache[key] || ( cache[key] = f.call(this, arg));
}
}
function multiple(f,keys,values,serializer,equals,change,maxArgs,...args) {
const result = {};
for(let i=0;i<keys.length;i++) {
let key = keys[i];
if(key===null) { result.index = i; continue; }
if(maxArgs) key = key.slice(0,maxArgs);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break;
if(j===max) {
result.index = i;
result.value = values[i];
}
}
}
}
const i = result.index>=0 ? result.index : values.length;
if(change) change(key,true);
return typeof result.value === "undefined" ? result.value = values[i] = f(...(keys[i] = args)) : result.value;
}
const deepEquals = require('lodash').isEqual;

@@ -249,3 +148,3 @@ const fastDeepEqual = require('fast-equals').deepEqual;

const mIMemoized = iMemoized.memoize(fibonacci);
const mMicro = micromemo(fibonacci);
const mNano = nanomemoize(fibonacci);

@@ -255,3 +154,3 @@

fibonacciSuite
/* .add('addy-osmani', () => {
.add('addy-osmani', () => {
mAddyOsmani(fibonacciNumber);

@@ -278,3 +177,3 @@ })

mIMemoized(fibonacciNumber);
})*/
})
.add('micro-memoize', () => {

@@ -289,8 +188,8 @@ mMicroMemoize(fibonacciNumber);

})
.add('micromemo', () => {
mMicro(fibonacciNumber);
.add('namomemoize', () => {
mNano(fibonacciNumber);
})
.on('start', () => {
console.log(''); // eslint-disable-line no-console
console.log('Starting cycles for functions with a single parameter...'); // eslint-disable-line no-console
console.log('Starting cycles for functions with a single primitive parameter...'); // eslint-disable-line no-console

@@ -312,2 +211,76 @@ results = [];

const runSingleParameterObjectSuite = () => {
const fibonacciSuite = new Benchmark.Suite('Single parameter');
const fibonacciNumber = Number(35);
const mUnderscore = underscore(fibonacci);
const mLodash = lodash(fibonacci);
const mRamda = ramda(fibonacci);
const mMemoizee = memoizee(fibonacci);
const mFastMemoize = fastMemoize(fibonacci);
const mAddyOsmani = addyOsmani(fibonacci);
const mMemoizerific = memoizerific(Infinity)(fibonacci);
const mLruMemoize = lruMemoize(Infinity)(fibonacci);
const mMoize = moize(fibonacci);
const mMicroMemoize = microMemoize(fibonacci);
const mIMemoized = iMemoized.memoize(fibonacci);
const mNano = nanomemoize(fibonacci);
return new Promise((resolve) => {
fibonacciSuite
.add('addy-osmani', () => {
mAddyOsmani(fibonacciNumber);
})
.add('lodash', () => {
mLodash(fibonacciNumber);
})
.add('lru-memoize', () => {
mLruMemoize(fibonacciNumber);
})
.add('memoizee', () => {
mMemoizee(fibonacciNumber);
})
.add('memoizerific', () => {
mMemoizerific(fibonacciNumber);
}) .add('ramda', () => {
mRamda(fibonacciNumber);
})
.add('underscore', () => {
mUnderscore(fibonacciNumber);
})
.add('iMemoized', () => {
mIMemoized(fibonacciNumber);
})
.add('micro-memoize', () => {
mMicroMemoize(fibonacciNumber);
})
.add('moize', () => {
mMoize(fibonacciNumber);
})
.add('fast-memoize', () => {
mFastMemoize(fibonacciNumber);
})
.add('namomemoize', () => {
mNano(fibonacciNumber);
})
.on('start', () => {
console.log(''); // eslint-disable-line no-console
console.log('Starting cycles for functions with a single object parameter...'); // eslint-disable-line no-console
results = [];
spinner.start();
})
.on('cycle', onCycle)
.on('complete', () => {
onComplete();
resolve();
})
.run({
async: true
});
});
};
const runMultiplePrimitiveSuite = () => {

@@ -326,7 +299,7 @@ const fibonacciSuite = new Benchmark.Suite('Multiple parameters (Primitive)');

const mIMemoized = iMemoized.memoize(fibonacciMultiplePrimitive);
const mMicro = micromemo(fibonacciMultiplePrimitive);
const mNano = nanomemoize(fibonacciMultiplePrimitive);
return new Promise((resolve) => {
fibonacciSuite
/*.add('addy-osmani', () => {
.add('addy-osmani', () => {
mAddyOsmani(fibonacciNumber, isComplete);

@@ -345,3 +318,3 @@ })

mMemoizerific(fibonacciNumber, isComplete);
})*/
})
.add('fast-memoize', () => {

@@ -356,4 +329,4 @@ mFastMemoize(fibonacciNumber, isComplete);

})
.add('micromemo', () => {
mMicro(fibonacciNumber, isComplete);
.add('nanomemoize', () => {
mNano(fibonacciNumber, isComplete);
})

@@ -393,7 +366,7 @@ .on('start', () => {

const mMicroMemoize = microMemoize(fibonacciMultipleObject);
const mMicro = micromemo(fibonacciMultipleObject);
const mNano = nanomemoize(fibonacciMultipleObject);
return new Promise((resolve) => {
fibonacciSuite
/* .add('addy-osmani', () => {
.add('addy-osmani', () => {
mAddyOsmani(fibonacciNumber, isComplete);

@@ -409,3 +382,3 @@ })

mMemoizerific(fibonacciNumber, isComplete);
})*/
})
.add('fast-memoize', () => {

@@ -420,4 +393,4 @@ mFastMemoize(fibonacciNumber, isComplete);

})
.add('micromemo', () => {
mMicro(fibonacciNumber,isComplete);
.add('nanomemoize', () => {
mNano(fibonacciNumber,isComplete);
})

@@ -461,6 +434,6 @@ .on('start', () => {

const mMicroDeep = micromemo(fibonacciMultipleDeepEqual, {
const mNanoDeep = nanomemoize(fibonacciMultipleDeepEqual, {
equals: deepEquals
});
const mMicroFastDeep = micromemo(fibonacciMultipleDeepEqual, {
const mNanoFastDeep = nanomemoize(fibonacciMultipleDeepEqual, {
equals: fastDeepEqual

@@ -480,7 +453,7 @@ });

})
.add('micromemo deep equals (lodash isEqual)', () => {
mMicroDeep(fibonacciNumber);
.add('nanomemoize deep equals (lodash isEqual)', () => {
mNanoDeep(fibonacciNumber);
})
.add('micromemo deep equals (fast-equals deepEqual)', () => {
mMicroFastDeep(fibonacciNumber);
.add('nanomemoize deep equals (fast-equals deepEqual)', () => {
mNanoFastDeep(fibonacciNumber);
})

@@ -509,4 +482,5 @@ .on('start', () => {

runSingleParameterSuite()
.then(runSingleParameterObjectSuite)
.then(runMultiplePrimitiveSuite)
.then(runMultipleObjectSuite)
.then(runAlternativeOptionsSuite);
(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={}) {
const {serializer=JSON.stringify,equals,maxAge,maxArgs,maxSize,stats} = options,
singles = {},
keys = [],
values = [],
changes = [],
change = (cache,key,property) => {
if(property) key = typeof(key) + "@" + key;
changes[key] = {key,cache};
const {
serializer = (value) => {
if(value && (typeof value === "string" || typeof value === "object")) {
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1"
return JSON.stringify(value);
}
return value;
},
hits = [],
hit = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
let record = hits[key];
if(!record) record = hits[key] = {count:0,cache};
hit.count++;
hit.time = Date.now();
equals,
maxAge,
maxArgs,
vargs = hasVargs(fn)
} = 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
c[key] = {key,cache};
},
timeouts = [],
timeout = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
if(timeouts[key]) clearTimeout(timeouts[key]);
timeouts[key] = setTimeout(() => cache[key]=timeouts[key]=null,maxAge);
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];
},maxAge);
};
let memoized;
if(fn.length===1 && !equals) {
memoized = single.bind(
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;
// pre-bind core arguments, faster than using a closure or passing on stack
if(unary) {
f = single.bind(
this,
fn,
singles,
(maxSize || maxAge || stats ? change.bind(this,values): null),
s,
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s
serializer
);
} else {
memoized = multiple.bind(
f = multiple.bind(
this,
fn,
keys,
values,
serializer,
equals ? equals : (a,b) => a===b,
(maxSize || maxAge || stats ? change.bind(this,values): null),
k,
v,
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
maxArgs
);
}
memoized.clear = () => {
Object.keys(singles).forEach(key => delete singles[key]);
keys.splice(0,keys.length);
values.splice(0,values.length);
changes.splice(0,changes.length);
Object.keys(changes).forEach(key => key==="length" || delete changes[key]);
timeouts.forEach(timeout => !timeout || clearTimeout(timeout));
timeouts.splice(0,timeouts.length);
Object.keys(timeouts).forEach(key => key==="length" || delete timeouts[key]);
// reset all the caches, must splice arrays or delete keys on objects to retain bind integrity
f.clear = () => {
Object.keys(s).forEach(k => delete s[k]);
k.splice(0,k.length);
v.splice(0,v.length);
Object.keys(c).forEach(k => delete c[k]);
Object.keys(t).forEach(k => { clearTimeout(t[k]); delete t[k]; });
}
memoized.keys = () => keys.slice();
memoized.values = () => values.slice();
memoized.keyValues = () => Object.assign({},singles);
return memoized;
f.keys = () => (!unary ? k.slice() : null);
f.values = () => (!unary ? v.slice() : null);
f.keyValues = () => (unary ? Object.assign({},s) : null);
return f;
}
// for single argument functions, just use a JS object key look-up
function single (f,cache,change,serializer,arg) {
if(arguments.length<=5) {
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg));
if(change) change(key,true);
return cache[key] || ( cache[key] = f.call(this, arg));
}
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" || arg.constructor===Number || arg.constructor===Boolean ? arg : serializer(arg));
if(change) change(key);
return cache[key] || ( cache[key] = f.call(this, arg));
}
function multiple(f,keys,values,serializer,equals,change,maxArgs,...args) {
const result = {};
for(let i=0;i<keys.length;i++) {
let key = keys[i];
if(key===null) { result.index = i; continue; }
if(maxArgs) key = key.slice(0,maxArgs);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break;
if(j===max) {
result.index = i;
result.value = values[i];
}
// for multiple 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,keys,values,equals,change,max,...args) {
const rslt = {};
for(let i=0;i<keys.length;i++) { // an array of arrays of args
let key = keys[i];
if(max) key = key.slice(0,max);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break; // go to next key if args don't match
if(j===max) { // the args matched
rslt.index = i;
rslt.value = values[i]; // get the cached value
}
}
}
const i = result.index>=0 ? result.index : values.length;
if(change) change(key,true);
return typeof result.value === "undefined" ? result.value = values[i] = f(...(keys[i] = args)) : result.value;
}
const i = rslt.index>=0 ? rslt.index : values.length;
if(change) change(i);
return typeof rslt.value === "undefined" ? rslt.value = values[i] = f(...(keys[i] = args)) : rslt.value;
}
if(typeof(module)!=="undefined") module.exports = nanomemoize;
if(typeof(window)!=="undefined") window.nanomemoize = nanomemoize;
if(typeof(module)!=="undefined") module.exports = nanomemoize;
if(typeof(window)!=="undefined") window.nanomemoize = nanomemoize;
}).call(this);
},{}]},{},[1]);

@@ -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";function nanomemoize(fn,options={}){const{serializer:serializer=JSON.stringify,equals:equals,maxAge:maxAge,maxArgs:maxArgs,maxSize:maxSize,stats:stats}=options,singles={},keys=[],values=[],changes=[],change=(cache,key,property)=>{if(property)key=typeof key+"@"+key;changes[key]={key:key,cache:cache}},hits=[],hit=(key,cache,property)=>{if(property)key=typeof key+"@"+key;let record=hits[key];if(!record)record=hits[key]={count:0,cache:cache};hit.count++;hit.time=Date.now()},timeouts=[],timeout=(key,cache,property)=>{if(property)key=typeof key+"@"+key;if(timeouts[key])clearTimeout(timeouts[key]);timeouts[key]=setTimeout(()=>cache[key]=timeouts[key]=null,maxAge)};let memoized;if(fn.length===1&&!equals){memoized=single.bind(this,fn,singles,maxSize||maxAge||stats?change.bind(this,values):null,serializer)}else{memoized=multiple.bind(this,fn,keys,values,serializer,equals?equals:(a,b)=>a===b,maxSize||maxAge||stats?change.bind(this,values):null,maxArgs)}memoized.clear=(()=>{Object.keys(singles).forEach(key=>delete singles[key]);keys.splice(0,keys.length);values.splice(0,values.length);changes.splice(0,changes.length);Object.keys(changes).forEach(key=>key==="length"||delete changes[key]);timeouts.forEach(timeout=>!timeout||clearTimeout(timeout));timeouts.splice(0,timeouts.length);Object.keys(timeouts).forEach(key=>key==="length"||delete timeouts[key])});memoized.keys=(()=>keys.slice());memoized.values=(()=>values.slice());memoized.keyValues=(()=>Object.assign({},singles));return memoized}function single(f,cache,change,serializer,arg){if(arguments.length<=5){const key=!arg||typeof arg==="number"||typeof arg==="boolean"?arg:serializer(arg);if(change)change(key,true);return cache[key]||(cache[key]=f.call(this,arg))}}function multiple(f,keys,values,serializer,equals,change,maxArgs,...args){const result={};for(let i=0;i<keys.length;i++){let key=keys[i];if(key===null){result.index=i;continue}if(maxArgs)key=key.slice(0,maxArgs);if(key.length===args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!equals(key[j],args[j]))break;if(j===max){result.index=i;result.value=values[i]}}}}const i=result.index>=0?result.index:values.length;if(change)change(key,true);return typeof result.value==="undefined"?result.value=values[i]=f(...keys[i]=args):result.value}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 hasVargs=f=>{const s=f+"",i=s.indexOf("...");return i>=0&&i<s.indexOf(")"||s.indexOf("arguments")>=0)};function nanomemoize(fn,options={}){const{serializer:serializer=(value=>{if(value&&(typeof value==="string"||typeof value==="object")){return JSON.stringify(value)}return 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.splice(0,k.length);v.splice(0,v.length);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}function single(f,cache,change,serializer,arg){const key=!arg||typeof arg==="number"||typeof arg==="boolean"||arg.constructor===Number||arg.constructor===Boolean?arg:serializer(arg);if(change)change(key);return cache[key]||(cache[key]=f.call(this,arg))}function multiple(f,keys,values,equals,change,max,...args){const rslt={};for(let i=0;i<keys.length;i++){let key=keys[i];if(max)key=key.slice(0,max);if(key.length===args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!equals(key[j],args[j]))break;if(j===max){rslt.index=i;rslt.value=values[i]}}}}const i=rslt.index>=0?rslt.index:values.length;if(change)change(i);return typeof rslt.value==="undefined"?rslt.value=values[i]=f(...keys[i]=args):rslt.value}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this)},{}]},{},[1]);

@@ -1,1 +0,1 @@

(function(){"use strict";function nanomemoize(fn,options={}){const{serializer:serializer=JSON.stringify,equals:equals,maxAge:maxAge,maxArgs:maxArgs,maxSize:maxSize,stats:stats}=options,singles={},keys=[],values=[],changes=[],change=(cache,key,property)=>{if(property)key=typeof key+"@"+key;changes[key]={key:key,cache:cache}},hits=[],hit=(key,cache,property)=>{if(property)key=typeof key+"@"+key;let record=hits[key];if(!record)record=hits[key]={count:0,cache:cache};hit.count++;hit.time=Date.now()},timeouts=[],timeout=(key,cache,property)=>{if(property)key=typeof key+"@"+key;if(timeouts[key])clearTimeout(timeouts[key]);timeouts[key]=setTimeout(()=>cache[key]=timeouts[key]=null,maxAge)};let memoized;if(fn.length===1&&!equals){memoized=single.bind(this,fn,singles,maxSize||maxAge||stats?change.bind(this,values):null,serializer)}else{memoized=multiple.bind(this,fn,keys,values,serializer,equals?equals:(a,b)=>a===b,maxSize||maxAge||stats?change.bind(this,values):null,maxArgs)}memoized.clear=(()=>{Object.keys(singles).forEach(key=>delete singles[key]);keys.splice(0,keys.length);values.splice(0,values.length);changes.splice(0,changes.length);Object.keys(changes).forEach(key=>key==="length"||delete changes[key]);timeouts.forEach(timeout=>!timeout||clearTimeout(timeout));timeouts.splice(0,timeouts.length);Object.keys(timeouts).forEach(key=>key==="length"||delete timeouts[key])});memoized.keys=(()=>keys.slice());memoized.values=(()=>values.slice());memoized.keyValues=(()=>Object.assign({},singles));return memoized}function single(f,cache,change,serializer,arg){if(arguments.length<=5){const key=!arg||typeof arg==="number"||typeof arg==="boolean"?arg:serializer(arg);if(change)change(key,true);return cache[key]||(cache[key]=f.call(this,arg))}}function multiple(f,keys,values,serializer,equals,change,maxArgs,...args){const result={};for(let i=0;i<keys.length;i++){let key=keys[i];if(key===null){result.index=i;continue}if(maxArgs)key=key.slice(0,maxArgs);if(key.length===args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!equals(key[j],args[j]))break;if(j===max){result.index=i;result.value=values[i]}}}}const i=result.index>=0?result.index:values.length;if(change)change(key,true);return typeof result.value==="undefined"?result.value=values[i]=f(...keys[i]=args):result.value}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this);
(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={}){const{serializer:serializer=(value=>{if(value&&(typeof value==="string"||typeof value==="object")){return JSON.stringify(value)}return 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.splice(0,k.length);v.splice(0,v.length);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}function single(f,cache,change,serializer,arg){const key=!arg||typeof arg==="number"||typeof arg==="boolean"||arg.constructor===Number||arg.constructor===Boolean?arg:serializer(arg);if(change)change(key);return cache[key]||(cache[key]=f.call(this,arg))}function multiple(f,keys,values,equals,change,max,...args){const rslt={};for(let i=0;i<keys.length;i++){let key=keys[i];if(max)key=key.slice(0,max);if(key.length===args.length){const max=key.length-1;for(let j=0;j<=max;j++){if(!equals(key[j],args[j]))break;if(j===max){rslt.index=i;rslt.value=values[i]}}}}const i=rslt.index>=0?rslt.index:values.length;if(change)change(i);return typeof rslt.value==="undefined"?rslt.value=values[i]=f(...keys[i]=args):rslt.value}if(typeof module!=="undefined")module.exports=nanomemoize;if(typeof window!=="undefined")window.nanomemoize=nanomemoize}).call(this);
(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={}) {
const {serializer=JSON.stringify,equals,maxAge,maxArgs,maxSize,stats} = options,
singles = {},
keys = [],
values = [],
changes = [],
change = (cache,key,property) => {
if(property) key = typeof(key) + "@" + key;
changes[key] = {key,cache};
const {
serializer = (value) => {
if(value && (typeof value === "string" || typeof value === "object")) {
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1"
return JSON.stringify(value);
}
return value;
},
hits = [],
hit = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
let record = hits[key];
if(!record) record = hits[key] = {count:0,cache};
hit.count++;
hit.time = Date.now();
equals,
maxAge,
maxArgs,
vargs = hasVargs(fn)
} = 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
c[key] = {key,cache};
},
timeouts = [],
timeout = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
if(timeouts[key]) clearTimeout(timeouts[key]);
timeouts[key] = setTimeout(() => cache[key]=timeouts[key]=null,maxAge);
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];
},maxAge);
};
let memoized;
if(fn.length===1 && !equals) {
memoized = single.bind(
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;
// pre-bind core arguments, faster than using a closure or passing on stack
if(unary) {
f = single.bind(
this,
fn,
singles,
(maxSize || maxAge || stats ? change.bind(this,values): null),
s,
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s
serializer
);
} else {
memoized = multiple.bind(
f = multiple.bind(
this,
fn,
keys,
values,
serializer,
equals ? equals : (a,b) => a===b,
(maxSize || maxAge || stats ? change.bind(this,values): null),
k,
v,
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
maxArgs
);
}
memoized.clear = () => {
Object.keys(singles).forEach(key => delete singles[key]);
keys.splice(0,keys.length);
values.splice(0,values.length);
changes.splice(0,changes.length);
Object.keys(changes).forEach(key => key==="length" || delete changes[key]);
timeouts.forEach(timeout => !timeout || clearTimeout(timeout));
timeouts.splice(0,timeouts.length);
Object.keys(timeouts).forEach(key => key==="length" || delete timeouts[key]);
// reset all the caches, must splice arrays or delete keys on objects to retain bind integrity
f.clear = () => {
Object.keys(s).forEach(k => delete s[k]);
k.splice(0,k.length);
v.splice(0,v.length);
Object.keys(c).forEach(k => delete c[k]);
Object.keys(t).forEach(k => { clearTimeout(t[k]); delete t[k]; });
}
memoized.keys = () => keys.slice();
memoized.values = () => values.slice();
memoized.keyValues = () => Object.assign({},singles);
return memoized;
f.keys = () => (!unary ? k.slice() : null);
f.values = () => (!unary ? v.slice() : null);
f.keyValues = () => (unary ? Object.assign({},s) : null);
return f;
}
// for single argument functions, just use a JS object key look-up
function single (f,cache,change,serializer,arg) {
if(arguments.length<=5) {
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg));
if(change) change(key,true);
return cache[key] || ( cache[key] = f.call(this, arg));
}
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" || arg.constructor===Number || arg.constructor===Boolean ? arg : serializer(arg));
if(change) change(key);
return cache[key] || ( cache[key] = f.call(this, arg));
}
function multiple(f,keys,values,serializer,equals,change,maxArgs,...args) {
const result = {};
for(let i=0;i<keys.length;i++) {
let key = keys[i];
if(key===null) { result.index = i; continue; }
if(maxArgs) key = key.slice(0,maxArgs);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break;
if(j===max) {
result.index = i;
result.value = values[i];
}
// for multiple 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,keys,values,equals,change,max,...args) {
const rslt = {};
for(let i=0;i<keys.length;i++) { // an array of arrays of args
let key = keys[i];
if(max) key = key.slice(0,max);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break; // go to next key if args don't match
if(j===max) { // the args matched
rslt.index = i;
rslt.value = values[i]; // get the cached value
}
}
}
const i = result.index>=0 ? result.index : values.length;
if(change) change(key,true);
return typeof result.value === "undefined" ? result.value = values[i] = f(...(keys[i] = args)) : result.value;
}
const i = rslt.index>=0 ? rslt.index : values.length;
if(change) change(i);
return typeof rslt.value === "undefined" ? rslt.value = values[i] = f(...(keys[i] = args)) : rslt.value;
}
if(typeof(module)!=="undefined") module.exports = nanomemoize;
if(typeof(window)!=="undefined") window.nanomemoize = nanomemoize;
}).call(this);
if(typeof(module)!=="undefined") module.exports = nanomemoize;
if(typeof(window)!=="undefined") window.nanomemoize = nanomemoize;
}).call(this);
{
"name": "nano-memoize",
"version": "v0.0.1a",
"description": "Faster than fast, smaller than micro ... a nano speed and size memoizer.",
"version": "v0.0.4b",
"description": "Faster than fast, smaller than micro ... a nano speed and nano size memoizer.",
"engines": {},
"license": "MIT",
"scripts": {
"prepare": "cp ./src/nano-memoize.js ./index.js && browserify ./src/nano-memoize.js -o browser/nano-memoize.js && uglifyjs browser/nano-memoize.js -o browser/nano-memoize.min.js && uglifyjs src/nano-memoize.js -o dist/nano-memoize.min.js"
"prepare": "cp ./src/nano-memoize.js ./index.js && cp ./src/nano-memoize.js dist/nano-memoize.js && browserify ./src/nano-memoize.js -o browser/nano-memoize.js && uglifyjs browser/nano-memoize.js -o browser/nano-memoize.min.js && uglifyjs src/nano-memoize.js -o dist/nano-memoize.min.js"
},

@@ -30,2 +30,3 @@ "repository": {

"chai": "^3.4.1",
"cli-table2": "^0.2.0",
"codacy-coverage": "^2.0.0",

@@ -46,8 +47,7 @@ "codeclimate-test-reporter": "^0.2.0",

"moize": "^4.0.4",
"ora": "^1.3.0",
"ramda": "^0.25.0",
"underscore": "^1.8.3"
},
"dependencies": {
}
"dependencies": {}
}

@@ -1,19 +0,128 @@

# Faster than fast, smaller than micro ... a nano speed and size memoizer.
# Faster than fast, smaller than micro ... nano-memoizer.
# Introduction
The great devs [caiogondim](https://github.com/caiogondim) and [planttheidea](https://github.com/planttheidea) have produced consistently fast memoizers. We analyzed their code to see if we could build something faster than [fast-memoize](https://github.com/caiogondim/fast-memoize.js) and smaller than [micro-memoize](https://github.com/planttheidea/micro-memoize) while adding back some of the functionality [moize](https://github.com/planttheidea/moize). We think we have done it. The test results below are from:
The devs [caiogondim](https://github.com/caiogondim) and [planttheidea](https://github.com/planttheidea) have produced great memoizers. We analyzed their code to see if we could build something faster than [fastmemoize](https://github.com/caiogondim/fastmemoize.js) and smaller than [micromemoize](https://github.com/planttheidea/micromemoize) while adding back some of the functionality of [moize](https://github.com/planttheidea/moize) removed in micro-memoize. We think we have done it ... but credit to them ... we just merged the best ideas in both and eliminated excess code.
Along the way we discovered some size and speed optimizations that could be made to [fast-memoize](https://github.com/caiogondim/fast-memoize.js) and will be making a pull request to [caiogondim](https://github.com/caiogondim) for those of you desiring to stick with a tried and true solution.
The minified/gzipped size is 920 bytes for `nano-memoize` vs 959 bytes for `micro-memoize`.
The speed tests are below.
`fast-memoize` is only the fastest in one case, single argument functions taking an object. However, in the case of a single primitve argument `nano-memoize` is 240% faster and in all other cases even better than that.
`micro-memoize` is faster than `fast-memoize` except for single argument functions.
`nano-memoize` is always faster than `moize`.
`nano-memoize` and `fast-memoize` are comparable for multiple-argument functions. `fast-memoize` is always the slowest for these.
We have found that benchmarks can vary dramartically from O/S to O/S or node version to node version. These tests were run on a Windows 10 64bit 2.4gx machine with 8GB RAM and Node v9.4.0.
Functions with a single primitive parameter...
| Name� � � � � | Ops / sec� �| Relative margin of error | Sample size |
|---------------|-------------|--------------------------|-------------|
| nano-memoize �| 183,478,889 |� �0.55%� � � � � � � � � | 90� � � � � |
| fast-memoize� | 75,218,544� |� �2.21%� � � � � � � � � | 81� � � � � |
| micro-memoize | 26,565,887� |� �1.48%� � � � � � � � � | 79� � � � � |
| moize� � � � �| 22,047,750� |� �0.48%� � � � � � � � � | 86� � � � � |
Functions with a single object parameter...
| Name� � � � � | Ops / sec� | Relative margin of error | Sample size |
|---------------|------------|--------------------------|-------------|
| fast-memoize� | 78,297,395 |� �0.54%� � � � � � � � � | 90� � � � � |
| nano-memoize �| 57,453,837 |� �2.03%� � � � � � � � � | 86� � � � � |
| micro-memoize | 26,615,102 |� �0.40%� � � � � � � � � | 91� � � � � |
| moize� � � � �| 21,760,403 |� �0.53%� � � � � � � � � | 84� � � � � |
Functions with multiple parameters that contain only primitives...
| Name� � � � � | Ops / sec� | Relative margin of error | Sample size |
|---------------|------------|--------------------------|-------------|
| nanomemoize� �| 18,280,535 |� �1.45%� � � � � � � � � | 83� � � � � |
| micro-memoize | 16,394,987 |� �0.95%� � � � � � � � � | 86� � � � � |
| moize� � � � �| 13,921,400 |� �0.93%� � � � � � � � � | 87� � � � � |
| fast-memoize� | 884,771� � |� �0.47%� � � � � � � � � | 94� � � � � |
Functions with multiple parameters that contain objects...
| Name� � � � � | Ops / sec� | Relative margin of error | Sample size |
|---------------|------------|--------------------------|-------------|
| micro-memoize | 16,244,817 |� �0.86%� � � � � � � � � | 89� � � � � |
| nanomemoize� �| 14,362,298 |� �1.89%� � � � � � � � � | 89� � � � � |
| moize� � � � �| 14,123,028 |� �0.62%� � � � � � � � � | 88� � � � � |
| fast-memoize� | 682,549� � |� �0.49%� � � � � � � � � | 90� � � � � |
Deep equals ...
| Name� � � � � � � � � � � � � � � � � � � � � � � | Ops / sec� | Relative margin of error | Sample size |
|---------------------------------------------------|------------|--------------------------|-------------|
| micro-memoize deep equals (hash-it isEqual)� � � �| 18,894,707 |� �0.86%� � � � � � � � � | 91� � � � � |
| nanomemoize deep equals (lodash isEqual)� � � � � | 18,504,364 |� �0.85%� � � � � � � � � | 90� � � � � |
| micro-memoize deep equals (fast-equals deepEqual) | 16,302,930 |� �0.73%� � � � � � � � � | 87� � � � � |
| nanomemoize deep equals (fast-equals deepEqual)� �| 15,162,874 |� �0.83%� � � � � � � � � | 89� � � � � |
We were puzzled about the multiple argument performance on `fast-memoize` given its stated goal of being the "fastest possible". We discovered that the default caching and serialization approach used by fast-memoize only performs well for single argument functions for two reasons:
1) It uses `toJSON` to create a key for an entire argument list. This can be slow.
2) Because a single key is generated for all arguments when perhaps only the first argument differs in a call, a lot of extra work is done. The `moize` and `micro-memoize` approach adopted by `nano-memoize` is far faster for multiple arguments.
Along the way we also discovered that fast-memoize is subject to a key generation risk on edge case functions and fixed the flaw. The fork is [here](https://github.com/anywhichway/fastmemoize.js). We have submitted a pull request. See this [Medium article](https://codeburst.io/akeytokeyswhenjavascriptkeysdontmatchab44c81adc87) for details.
# Usage
nmp install nano-memoize
use the code in the `browser` directory for the browser
Since most devs are running a build pipeline, the code is not transpiled, although it is browserified
# API
The API is a subset of the `moize` API. Documentation coming.
The API is a subset of the `moize` API.
# Release History (rverse chronological order)
```javascript
const memoized = micromemoize(sum(a,b) => a + b);
memoized(1,2); // 3
memoized(1,2); // pulled from cache
```
2018-01-24 v0.0.1a - First public release. Benchmark code in repository not yet running.
`memoized(function,options) returns function`
The shape of options is:
```javascript
{
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.
}
```
# Release History (reverse chronological order)
2018-01-27 v0.0.4b BETA Fixed benchmarks. Removed maxSize. More unit tests. Fixed maxAge.
2018-01-27 v0.0.3b BETA More unit tests. Documentation. Benchmark code in repository not yet running.
2018-01-24 v0.0.2a ALPHA Minor speed enhancements. 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 s = f+"",
i = s.indexOf("...");
return i>=0 && i<s.indexOf(")" || s.indexOf("arguments")>=0);
}
function nanomemoize (fn, options={}) {
const {serializer=JSON.stringify,equals,maxAge,maxArgs,maxSize,stats} = options,
singles = {},
keys = [],
values = [],
changes = [],
change = (cache,key,property) => {
if(property) key = typeof(key) + "@" + key;
changes[key] = {key,cache};
const {
serializer = (value) => {
if(value && (typeof value === "string" || typeof value === "object")) {
// strings must be stringified because cache[1] should not equal or overwrite cache["1"] for value = 1 and value = "1"
return JSON.stringify(value);
}
return value;
},
hits = [],
hit = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
let record = hits[key];
if(!record) record = hits[key] = {count:0,cache};
hit.count++;
hit.time = Date.now();
equals,
maxAge,
maxArgs,
vargs = hasVargs(fn)
} = 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
c[key] = {key,cache};
},
timeouts = [],
timeout = (key,cache,property) => {
if(property) key = typeof(key) + "@" + key;
if(timeouts[key]) clearTimeout(timeouts[key]);
timeouts[key] = setTimeout(() => cache[key]=timeouts[key]=null,maxAge);
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];
},maxAge);
};
let memoized;
if(fn.length===1 && !equals) {
memoized = single.bind(
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;
// pre-bind core arguments, faster than using a closure or passing on stack
if(unary) {
f = single.bind(
this,
fn,
singles,
(maxSize || maxAge || stats ? change.bind(this,values): null),
s,
(maxAge ? change.bind(this,s): null), // turn change logging on and bind to arg cache s
serializer
);
} else {
memoized = multiple.bind(
f = multiple.bind(
this,
fn,
keys,
values,
serializer,
equals ? equals : (a,b) => a===b,
(maxSize || maxAge || stats ? change.bind(this,values): null),
k,
v,
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
maxArgs
);
}
memoized.clear = () => {
Object.keys(singles).forEach(key => delete singles[key]);
keys.splice(0,keys.length);
values.splice(0,values.length);
changes.splice(0,changes.length);
Object.keys(changes).forEach(key => key==="length" || delete changes[key]);
timeouts.forEach(timeout => !timeout || clearTimeout(timeout));
timeouts.splice(0,timeouts.length);
Object.keys(timeouts).forEach(key => key==="length" || delete timeouts[key]);
// reset all the caches, must splice arrays or delete keys on objects to retain bind integrity
f.clear = () => {
Object.keys(s).forEach(k => delete s[k]);
k.splice(0,k.length);
v.splice(0,v.length);
Object.keys(c).forEach(k => delete c[k]);
Object.keys(t).forEach(k => { clearTimeout(t[k]); delete t[k]; });
}
memoized.keys = () => keys.slice();
memoized.values = () => values.slice();
memoized.keyValues = () => Object.assign({},singles);
return memoized;
f.keys = () => (!unary ? k.slice() : null);
f.values = () => (!unary ? v.slice() : null);
f.keyValues = () => (unary ? Object.assign({},s) : null);
return f;
}
// for single argument functions, just use a JS object key look-up
function single (f,cache,change,serializer,arg) {
if(arguments.length<=5) {
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" ? arg : serializer(arg));
if(change) change(key,true);
return cache[key] || ( cache[key] = f.call(this, arg));
}
const key = (!arg || typeof arg === "number" || typeof arg ==="boolean" || arg.constructor===Number || arg.constructor===Boolean ? arg : serializer(arg));
if(change) change(key);
return cache[key] || ( cache[key] = f.call(this, arg));
}
function multiple(f,keys,values,serializer,equals,change,maxArgs,...args) {
const result = {};
for(let i=0;i<keys.length;i++) {
let key = keys[i];
if(key===null) { result.index = i; continue; }
if(maxArgs) key = key.slice(0,maxArgs);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break;
if(j===max) {
result.index = i;
result.value = values[i];
}
// for multiple 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,keys,values,equals,change,max,...args) {
const rslt = {};
for(let i=0;i<keys.length;i++) { // an array of arrays of args
let key = keys[i];
if(max) key = key.slice(0,max);
if(key.length===args.length) {
const max = key.length - 1;
for(let j=0;j<=max;j++) {
if(!equals(key[j],args[j])) break; // go to next key if args don't match
if(j===max) { // the args matched
rslt.index = i;
rslt.value = values[i]; // get the cached value
}
}
}
const i = result.index>=0 ? result.index : values.length;
if(change) change(key,true);
return typeof result.value === "undefined" ? result.value = values[i] = f(...(keys[i] = args)) : result.value;
}
const i = rslt.index>=0 ? rslt.index : values.length;
if(change) change(i);
return typeof rslt.value === "undefined" ? rslt.value = values[i] = f(...(keys[i] = args)) : rslt.value;
}
if(typeof(module)!=="undefined") module.exports = nanomemoize;
if(typeof(window)!=="undefined") window.nanomemoize = nanomemoize;
}).call(this);
if(typeof(module)!=="undefined") module.exports = nanomemoize;
if(typeof(window)!=="undefined") window.nanomemoize = nanomemoize;
}).call(this);

@@ -22,2 +22,5 @@ var chai,

varArg = nanomemoize((...args) => args);
describe("Test",function() {

@@ -31,2 +34,48 @@ it("single primitive arg cached",function() {

});
it("single object arg cached",function() {
const value = {p1:1},
result = singleArg(value);
expect(result).to.equal(value);
});
it("multiple arg primitive cached",function() {
const result = multipleArg(1,2);
expect(result.arg1).to.equal(1);
expect(result.arg2).to.equal(2);
});
it("multiple arg object cached",function() {
const arg1 = {arg:1},
arg2 = {arg:2},
result = multipleArg(arg1,arg2);
expect(result.arg1.arg).to.equal(1);
expect(result.arg2.arg).to.equal(2);
});
it("multiple arg works with single",function() {
const arg1 = {arg:1};
result = multipleArg(arg1);
expect(result.arg1.arg).to.equal(1);
});
it("auto-detect vArg",function() {
const arg1 = 1, arg2 = 2;
expect(varArg.keyValues()).to.equal(null);
expect(Array.isArray(varArg.values())).to.equal(true);
expect(Array.isArray(varArg(arg1,arg2))).to.equal(true);
});
it("expires content",function(done) {
const expiring = nanomemoize((a) => a,{maxAge:5});
expect(expiring(1)).to.equal(1);
expect(expiring.keyValues()[1]).to.equal(1);
setTimeout(() => {
expect(expiring.keyValues()[1]).to.equal(undefined);
done();
},20)
});
it("clear cache",function() {
const value = 1;
expect(singleArg(value)).to.equal(value);
expect(singleArg.keyValues()[value]).to.equal(value);
singleArg.clear();
expect(singleArg.keyValues()[value]).to.equal(undefined);
expect(singleArg(value)).to.equal(value);
expect(singleArg.keyValues()[value]).to.equal(value);
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc