npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

auto-memoize

v1.0.14

Published

Memoize function with predifined caching strategies

Downloads

31

Readme

Fast javascript memoize function, with optimized memoize strategies provided. Class and methods memoize decorators.

Build Status Coverage Status npm version

npm i -S auto-memoize
import { memoize, CreateOnce, CallOnce } from 'auto-memoize'
import { fibo } from './fibonaci'

// memoize function
let memoizeFibonaci = memoize(fibo)
memoizeFibonaci(5)
memoizeFibonaci(5) //cache hit

// Create one instance with same parameters
@CreateOnce
class Person {
  firstName
  lastName
  constructor(firstName, lastName) {
    console.log(`Creating ${firstName} ${lastName}`)
    this.firstName = firstName,
    this.lastName = lastName
  }
  // call method once with same parameters
  @CallOnce
  getGreeting(greet: string) {
    console.log(greet)
    const name = `${this.firstName} ${this.lastName}`
    return { name, greeting }
  }
}
const person1 = new Person('Boris', 'Johnson') 
// logged 'Creating Boris Johnson'
const person2 = new Person('Boris', 'Johnson')
person1 == person2 // true

const g1 = person1.getGreeting('hi') // logged 'hi'
const g2 = person1.getGreeting('hi')
g1 == g2 // true

memoize

Default implementation

Caches by all parameters as a key, comparing them with Object.is algorithm

const calc = require('expensive-calculation')
const { memoize } = require('auto-memoize')

const memoCalc = memoize(calc)

const param = {a: 'one'}
memoCalc(param, 1)
memoCalc(param, 2)
memoCalc(param, 1) // cache hit
memoCalc({a: 'one'}, 1) // no cache hit, because reference is different

WeakMap implementation

Caches by first parameter as a key and using ES6 WeakMap as a cache. Garbage collector automatically removes entries from cache, when no references for keys are present.

const calc = require('expensive-calculation')
const { memoize } = require('auto-memoize')

const memoCalc = memoize(calc, 'weak')

const param = {a: 'one'}
memoCalc(param, 1)
memoCalc(param, 2) // cache hit
memoCalc({a: 'one'}, 1) // no cache hit, because reference is different

Deep comparison implementation

Caches by all parameters as a key and compares them by content, if references are different. fast-deep-equal npm package does the comparison. It is performing better on big objects, than JSON.stringify.

const calc = require('expensive-calculation')
const { memoize } = require('auto-memoize')

const memoCalc = memoize(calc, 'deep')

const param = {a: 'one'}
memoCalc(param)
memoCalc(param) // cache hit
memoCalc({a: 'one'}) // cache hit

String key implementation

Caches all parameters as a string key. For cache is used ES6 Map. JSON.stringify is used to turn every parameter to string. It is useful for function with several parameters, but not big objects, that takes time to turn into string.

const calc = require('expensive-calculation')
const { memoize } = require('auto-memoize')

const memoCalc = memoize(calc, 'string')

const param = {a: 'one'}
memoCalc(param, 1)
memoCalc(param, 1) // cache hit
memoCalc({a: 'one'}, 2) 
memoCalc({a: 'one'}, 1) // cache hit

Custom key implementation

Caches by key from function, that returns string. For cache is used ES6 Map

const calc = require('expensive-calculation')
const { memoize } = require('memoize.js')

const memoCalc = memoize(calc, (p) => p.a)

const param = {a: 'one'}
memoCalc(param)
memoCalc(param) // cache hit
memoCalc({a: 'one'}) // cache hit

Benchmarking strategies

Small object parameter

const simple = {
  data: {
    p1: {
      name: 'auto-memoize'
      }
    }
  }
memoized(simple, 1, 1)

| | Strategy | ops/sec | | --- | -------- | --------- | | 1 | callback | 4,765,946 | | 2 | weak | 3,751,169 | | 3 | deep | 1,211,864 | | 4 | default | 1,004,562 | | 5 | string | 341,206 |

Object parameter

const data = { ...3 package.json }
memoized(data, 1, 1)

| | Strategy | ops/sec | | --- | -------- | --------- | | 1 | callback | 5,131,611 | | 2 | weak | 2,663,508 | | 3 | default | 992,947 | | 4 | deep | 832,758 | | 5 | string | 33,763 |

Different reference object parameter

const data = { ...3 package.json }
memoized(Object.assign({}, data), 1, 1)

| | Strategy | ops/sec | | | --- | -------- | --------- | --- | | 1 | callback | 2,195,159 | | | 2 | deep | 708,301 | | | 3 | string | 33,098 | | | | weak | 833,162 | n/a | | | default | 1,484 | n/a |

Primitive parameters

memoized("argument", 1, true)

| | Strategy | ops/sec | | | --- | -------- | --------- | --- | | 1 | callback | 3,340,699 | | | 2 | deep | 1,320,397 | | | 3 | default | 1,330,912 | | | 4 | string | 548,254 | | | | weak | | n/a |

Typings includes types for:

  • memoized function parameters
  • memoized function return type

getCache

Utility function to get cache instance from memoized function, class or method. It will return instance of CacheMap. It enables you to retrieve cache records and invalidate cache.

CacheMap

For different cache strategies different keys are applicable. Key types:

  • default - any[]
  • weak - object
  • string - string (parameters joined by -)
  • deep - any[]
  • custom key - string
export interface CacheMap<K = any, V = any> {
    get(key: K): V | undefined
    has(key: K): boolean
    set(key: K, result: V): this
    clear(): void
}

Examples

import { getCache, CreateOnce, CallOnce, memoize } from 'auto-memoize'
import { fibo } from './fibonaci'

const memo = memoize(fibo)
memo(5)

// cache from meoized function
getCache(memo).has([5]) // true
getCache(memo).clear()
getCache(memo).has([5]) // false

@CreateOnce
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
  @CallOnce
  greet(greeting) {
    const { firstName } = this
    return { greeting, firstName }
  }
}
const person = new Person('A', 'B')
person.greet('Hello')

// cache from  decorated 'CreateOnce' or 'CreateOnceBy' class
getCache(Person)

// cache from  decorated 'CallOnce' or 'CallOnceBy' class
getCache(person.greet)

Decorators

Docs

@CreateOnce

Class Decorator used to create same instance of class when same parameters are passed. It follows functional design pattern memoize, also Flyweight design pattern, when one instance of Class is created once with exact state. It defines how objects with state can be shared. If class constructor has no parameters, class decorated with CreateOnce becomes singleton. If this class is extended, effect of decorator will be applied on child classes. Parent constructor will always be constructor that creates class instance. Even if child constructor accepts different parameters. If child class is decorated, child class will have cache effect. It will cache instances by default 'auto-memoize' strategy. Decorator @CreateOnceBy can be configured with other cache hit strategy

@CreateOnceBy

Class Decorator used to created same instance of class when same parameters are passed. It follows functional design pattern memoize, also Flyweight design pattern, when one instance of Class is created once with exact state. It defines how objects with state can be shared. It will cache instances by strategy from decorator parameter. Decorator @CreateOnce is preconfigured with default cache hit strategy. If this class is extended, effect of decorator will be applied on child classes. Parent constructor will always be constructor that creates class instance. Even if child constructor accepts different parameters. If child class is decorated, child class will have cache effect.

@CallOnce

Class method decorator used to create memoized class method. Method that will return same output with same parameters passed. It follows memoize functional design pattern. It will cache by default auto-memoize strategy. Decorator @CallOnceBy can be configured with other cache hit strategy.

@CallOnceBy

Class method decorator used to create 'memoized' class method. Method that will return same output with same parameters passed. It follows memoize functional design pattern. It must be configured with cache hit strategy. Decorator @CallOnce is already preconfgured with default strategy.

Examples

Caching with default strategy

import { CreateOnce, CallOnce } from 'auto-memoize';

// Creates one class instance per constructor parameter 
@CreateOnce
class Greeter {
  greeting
    constructor(greeting) {
      this.greeting = greeting
    }

    // Calls method once per parameter unique
    // method with cache
    @CallOnce
    greet(name) {
      return { greet: `${this.greeting}, ${name}!` }
    }
}

const greeter1 = new Greeter('Hello')
const greeter2 = new Greeter('Hello')
greeter1 == greeter2 // true

const greet1 = greeter1.greet('Boris')
const greet2 = greeter1.greet('Boris')
const greet3 = greeter2.greet('Boris')
greet1 == greet2 // true
greet1 == greet3 // true

Caching with provided strategy

import { CreateOnceBy, CallOnceBy } from 'auto-memoize';
 
// Creates one class instance per constructor parameter 
@CreateOnceBy('string')
class CustomGreeter {
  greeting
    constructor(greeting) {
      this.greeting = greeting
    }

    // Calls method once per parameter unique
    // method with cache
    @CallOnceBy(person => person.name)
    greet(person) {
      return { greet: `${this.greeting}, ${person.name}!` }
    }
}
  const greeter1 = new Greeter('Hello')
  const greeter2 = new Greeter('Hello')
  greeter1 == greeter2 // true
  
  const greet1 = greeter1.greet({name: 'Boris'})
  const greet2 = greeter1.greet({name: 'Boris'})
  const greet3 = greeter2.greet({name: 'Boris'})
  greet1 == greet2 // true
  greet1 == greet3 // true

Class Singleton

import { CreateOnce } from 'auto-memoize';

// Always will return same instance of class
@CreateOnce
class Greeter {
  constructor() {
    }
    greet(person) {
      return { greet: `Hello, ${person.name}!` }
    }
}
const greeter1 = new Greeter()
const greeter2 = new Greeter()
greeter1 == greeter2 // true