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

@mitranim/jol

v0.1.6

Published

JS Collection Classes. Tiny extensions on JS built-in classes such as `Object`, `Array`, `Set`, `Map`, with nice features such as: easy-to-use typed collections with automatic idempotent element instantiation; dictionary with support for structured keys w

Downloads

9

Readme

Overview

"JS Collection Classes". Tiny extensions on JS built-in classes such as Object, Array, Set, Map, with nice features such as:

  • Easy-to-use typed collections such as Arr<Cls> or Set<Cls> with automatic idempotent element instantiation.
  • EqDict: dictionary with support for structured keys, like ["composite", "key"]. Compares keys by value, not by reference.
  • Consistent constructor signatures: everything is new Cls(val), like ES6 Map and Set.

Tiny, dependency-free, single file, native JS module.

Browser compatibility: any ES6+ environment. For older browsers, polyfill Set and Map and allow your bundler to transpile this library.

TOC

Usage

Install with NPM, or import by URL:

npm i -E @mitranim/jol
import * as j from '@mitranim/jol'
import * as j from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/jol.mjs'

API

class Null

Inherits from null rather than Object. Extend Null for a "squeaky clean" class.

class Arr extends Array

Like Array, but instead of new Array(...vals), the constructor signature is new Arr(vals), mirroring the behavior of Set and preventing gotchas.

Valid calls:

new j.Arr()             // Like [].
new j.Arr(null)         // Like [].
new j.Arr(0)            // Like [].
new j.Arr(8)            // Like Array(8).
new j.Arr([10, 20, 30]) // Like [10, 20, 30] or Array(10, 20, 30).
new j.Arr('one')        // Like [...'one'] or Array(...'one').

Invalid calls (runtime exception):

new j.Arr({})
new j.Arr(10, 20)
new j.Arr(() => {})

class ClsArr extends Arr

Runtime approximation of Arr<Cls>. Idempotently auto-instantiates values via this.cls.

class Model {
  constructor(val) {this.val = val * 2}
}

class Models extends j.ClsArr {
  get cls() {return Model}
}

new Models([10, 20])
// [ Model { val: 20 }, Model { val: 40 } ]

class Dict extends Map

Variant of Map whose behavior is closer to Object:

  • Can be constructed from plain objects: new j.Dict({one: 10}).
  • Compatible with JSON. Reversible encoding and decoding.
  • Allows only strings as keys. Other keys cause exceptions.

Compatibility with JSON:

new j.Dict({one: 10, two: 20})
// Dict { "one" => 10, "two" => 20 }

new j.Dict(JSON.parse(`{"one": 10, "two": 20}`))
// Dict { "one" => 10, "two" => 20 }

JSON.stringify(new j.Dict({one: 10, two: 20}))
// {"one":10,"two":20}

class ClsDict extends Dict

Runtime approximation of Dict<Cls>. Idempotently auto-instantiates values via this.cls.

class Model {
  constructor(val) {this.val = val * 2}
}

class Models extends j.ClsDict {
  get cls() {return Model}
}

new Models({one: 10, two: 20})
// Models { "one" => Model { val: 20 }, "two" => Model { val: 40 } }

class ClsMap extends Map

Runtime approximation of Map<any, Cls>. Idempotently auto-instantiates values via this.cls.

class Model {
  constructor(val) {this.val = val * 2}
}

class Models extends j.ClsMap {
  get cls() {return Model}
}

new Models([[10, 20], [30, 40]])
// Models { 10 => Model { val: 40 }, 30 => Model { val: 80 } }

class EqDict

Like Object or Map, but:

  • Keys can be structured data, such as {one: 10} or ['two', 'three'].
  • Keys are always compared by structure, not by reference.
  • Internally, keys are encoded as deterministic JSON.
    • Relies on engine quirks; has not been tested in all browsers.
    • The conversion function toKey is exported separately.
    • Computational complexity for access-by-key should be similar to {}, with the added overhead of JSON encoding (should scale with key size but not overall dict size).

Methods are similar to Map:

  • eqMap.has(key)
  • eqMap.get(key)
  • eqMap.set(key, val)
  • eqMap.delete(key)
  • eqMap.forEach()
  • eqMap.keys()
  • eqMap.values()
  • eqMap.entries()
  • Supports for .. of.

These internal methods require plain string keys, and are provided for subclasses:

  • eqMap.hasRaw(key)
  • eqMap.getRaw(key)
  • eqMap.setRaw(key, val)
  • eqMap.deleteRaw(key)

Unlike the syntax object[key], methods of EqDict operate only on own enumerable properties. Because keys are always JSON-encoded, there is no collision with Object methods and properties.

const coll = new j.EqDict()
coll.set({one: 10, two: 20}, 'value')
coll.get({two: 20, one: 10}) === 'value'

Supports reversible JSON encoding and decoding:

const prev = new j.EqDict().set('one', 10)
const next = new j.EqDict(JSON.parse(JSON.stringify(prev)))
// prev ≈ next

However, avoid the following:

new j.EqDict({one: 10})

All keys must be created by calling .set(), otherwise they don't get encoded, and you can't retrieve the values with .get()!

class ClsSet extends Set

Runtime approximation of Set<Cls>. Idempotently auto-instantiates values via this.cls.

class Model {
  constructor(val) {this.val = val * 2}
}

class Models extends j.ClsSet {
  get cls() {return Model}
}

new Models([10, 20])
// Models { Model { val: 20 }, Model { val: 40 } }

class Que extends Set

Ordered FIFO queue of functions. Paused by default. Calling .flush() immediately dequeues and calls functions one-by-one, and any future calls to .add() will result in immediate calls. Call .pause() to pause again.

const que = new Que()

que.add(function one() {console.log('one')})
que.add(function two() {console.log('two')})

// Prints 'one'.
// Prints 'two'.
que.flush()

// Prints 'three'.
que.add(function three() {console.log('three')})

que.pause()

function assign(target, source)

Similar to Object.assign, but with differences:

  • target must be a non-array object (internally termed "struct").
  • source must be either nil (undefined or null), a plain dict ({} or Object.create(null)), or a subclass of target.constructor.
  • Does not shadow inherited or non-enumerable properties.

Valid calls:

class Mock {
  constructor() {this.key = 'val'}
}

j.assign({},            undefined)
j.assign({},            {key: 'val'})
j.assign({},            new Mock())
j.assign(new Mock(),    {key: 'val'})
j.assign(new Mock(),    new Mock())
j.assign(new j.Obj({}), {key: 'val'})
j.assign({},            new j.Obj({key: 'val'}))

Invalid calls (runtime exception):

class Mock {}

j.assign(10,            {})
j.assign('one',         {})
j.assign({},            [])
j.assign([],            {})
j.assign([],            [])
j.assign(new Mock(),    new j.Obj({}))
j.assign(new j.Obj({}), new Mock())

Non-shadowing behavior:

j.assign({}, {constructor: 10, toString: 20, unknown: 30})
// {unknown: 30}

function inst(val, cls)

Idempotently converts val to an instance of cls:

  • If val is an instance of cls, returns val as-is.
  • Otherwise returns new cls(val).
class Mock {}

let val
val = j.inst(val, Mock) // new instance
val = j.inst(val, Mock) // preserves existing instance
val = j.inst(val, Mock) // preserves existing instance
val = j.inst(val, Mock) // preserves existing instance

function opt(val, cls)

Same as inst, but if val is null or undefined, it's returned as-is, without instantiation.

class Mock {constructor(val) {this.val = val}}
j.opt(undefined, Mock) // undefined
j.opt(10, Mock)        // Mock{val: 10}

function toKey(key)

Used internally by ClsArr, ClsSet, ClsMap. Like JSON.stringify but:

  • When input is undefined, returns '' instead of undefined.
  • Sorts object keys, producing deterministic output.
j.toKey()                   // ''
j.toKey(null)               // 'null'
j.toKey(10)                 // '10'
j.toKey('one')              // '"one"'
j.toKey({one: 10, two: 20}) // '{"one":10,"two":20}'
j.toKey({two: 20, one: 10}) // '{"one":10,"two":20}'

isPlain(val)

Returns true if val is either:

  • Primitive.
  • Instance of Array.
  • Plain dict: {} or Object.create(null).

Used internally by toKey, which rejects other inputs (runtime exception).

Changelog

0.1.6

Additions:

  • Add Dict.
  • Add ClsDict.

Breaking:

  • Removed Obj.
  • assign now avoids shadowing inherited or non-enumerable properties.
  • assign now allows nil source, doing nothing instead of throwing.

Misc:

  • Overridden methods of Arr, ClsArr, ClsMap, ClsSet now return this instead of undefined.
  • Que..pause and Que..flush now return this.

0.1.5

Renamed toInstinst, toInstOptopt for brevity.

0.1.4

toInst now allows subclass instances, instead of requiring an exact class match. This affects the behavior of all class collections.

0.1.3

Avoid an infinite loop when using NaN in toKey and EqDict.

0.1.2

new Null() now also creates a null-based object, rather than {}. The behavior of Null subclasses is unchanged.

0.1.1

Added toInstOpt. EqDict now extends Object rather than Null.

License

https://unlicense.org

Misc

I'm receptive to suggestions. If this library almost satisfies you but needs changes, open an issue or chat me up. Contacts: https://mitranim.com/#contacts