Skip to content

Latest commit

 

History

History
306 lines (217 loc) · 8.71 KB

coll_readme.md

File metadata and controls

306 lines (217 loc) · 8.71 KB

Overview

coll.mjs provides extended versions of JS data classes such as Set and Map, with better and/or additional APIs.

Port and rework of https://github.com/mitranim/jol.

TOC

Usage

import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/coll.mjs'

API

function bset

Links: source; test/example.

Same as new #Bset but syntactically shorter and a function.

function bsetOf

Links: source; test/example.

Same as #Bset .of but syntactically shorter and a function. The following is equivalent:

c.bsetOf(10, 20, 30)
c.Bset.of(10, 20, 30)
new c.Bset([10, 20, 30])
new c.Bset().add(10).add(20).add(30)

class Bset

Links: source; test/example.

Short for "better set". Variant of built-in Set with additional common-sense behaviors:

  • Supports JSON encoding, behaving like an array.
  • Supports adding other collections at any time by calling .mut, not just in the constructor.
  • Has additional instantiation shortcuts such as static .of.

function bmap

Links: source; test/example.

Same as new #Bmap but syntactically shorter and a function.

function bmapOf

Links: source; test/example.

Same as #Bmap .of but syntactically shorter and a function. The following is equivalent:

c.bmapOf(10, 20, 30, 40)
c.Bmap.of(10, 20, 30, 40)
new c.Bmap([[10, 20], [30, 40]])
new c.Bmap().set(10, 20).set(30, 40)

class Bmap

Links: source; test/example.

Short for "better map". Variant of built-in Map with additional common-sense behaviors:

  • Supports plain_dicts:
    • Can be instantiated from a dict.
    • Can be patched by a dict by calling .mut.
    • Can be converted to a dict by calling .toDict.
    • Behaves like a dict in JSON.
  • Supports JSON encoding. Only entries with string keys are sent to JSON, other entries are ignored.
  • Adding entries from another collection can be done any time by calling .mut, not just in the constructor.
  • Has additional instantiation shortcuts such as static .of.

class TypedMap

Links: source; test/example.

Variant of #Bmap with support for key and value checks. Subclasses must override methods .reqKey and .reqVal. These methods are automatically called by .set. Method .reqKey must validate and return the given key, and method .reqVal must validate and return the given value. Use type assertions provided by lang.

import * as l from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/lang.mjs'
import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/coll.mjs'

class StrNatMap extends c.TypedMap {
  reqKey(key) {return l.reqStr(key)}
  reqVal(val) {return l.reqNat(val)}
}

function pkOpt

Links: source; test/example.

Short for "primary key optional". Takes an arbitrary value and returns its "primary key". This is used internally by #Coll and #ClsColl.

Currently this uses the following interface:

interface Pkable {pk(): any}

Example use:

class Person {
  constructor({name}) {this.name = name}
  pk() {return this.name}
}

console.log(c.pkOpt(new Person({name: `Kara`})))
// 'Kara'

function pk

Links: source; test/example.

Short for "primary key". Similar to #pkOpt, but the input must produce a non-nil primary key, otherwise this panics. This is used internally by #Coll and #ClsColl.

c.pk({})
// Uncaught TypeError: unable to get primary key of {}

class Person {
  constructor({name}) {this.name = name}
  pk() {return this.name}
}

c.pk(new Person({name: `Mira`}))
// 'Mira'

class Coll

Links: source; test/example.

Short for "collection". Ordered map where values are indexed on their "primary key" determined by the function #pk which is also exported by this module. Unlike a normal JS map, this is considered a sequence of values, not a sequence of key-value pairs. Order is preserved, iterating the values is decently fast, and the index allows fast access by key without additional iteration.

class Person {
  constructor({name}) {this.name = name}
  pk() {return this.name}
}

const coll = new c.Coll()
  .add(new Person({name: `Mira`}))
  .add(new Person({name: `Kara`}))

console.log(coll)
/*
Coll {
  "Mira" => Person { name: "Mira" },
  "Kara" => Person { name: "Kara" },
}
*/

console.log([...coll])
/*
[
  Person { name: "Mira" },
  Person { name: "Kara" },
]
*/

class ClsColl

Links: source; test/example.

Variant of #Coll where values must belong to a specific class, determined by its getter cls. The default element class is Object. Override it when subclassing. Elements added with .add are idempotently instantiated.

Also see #ClsVec.

class Person {
  constructor({name}) {this.name = name}
  pk() {return this.name}
}

class Persons extends c.ClsColl {
  get cls() {return Person}
}

const coll = new Persons()
  .add({name: `Mira`})
  .add({name: `Kara`})

console.log(coll)

/*
Persons {
  "Mira" => Person { name: "Mira" },
  "Kara" => Person { name: "Kara" },
}
*/

class Vec

Links: source; test/example.

Short for "vector". Thin wrapper around a plain array. Features:

  • Implements the iterable interface.
  • Compatible with spread operator ....
  • Compatible with for of.
  • JSON-encodes like an array.
  • Can wrap a pre-existing array.

Differences and advantages over Array:

  • Better constructor signature.
    • Constructor takes exactly one argument, which is either nil or an array.
    • For comparison, the Array constructor has special cases that make subclassing difficult.
  • Can be subclassed without trashing performance.
    • At the time of writing, subclasses of Array suffer horrible deoptimization in V8.
    • Vec always wraps a true, avoiding this problem.

The overhead of the wrapper is insignificant.

import * as c from 'https://cdn.jsdelivr.net/npm/@mitranim/[email protected]/coll.mjs'

console.log(new c.Vec())
// Vec{$: []}

console.log(new c.Vec([10, 20, 30]))
// Vec{$: [10, 20, 30]}

console.log(c.Vec.of(10, 20, 30))
// Vec{$: [10, 20, 30]}

console.log(c.Vec.from(new Set([10, 20, 30])))
// Vec{$: [10, 20, 30]}

for (const val of c.Vec.of(10, 20, 30)) console.log(val)
// 10 20 30

class ClsVec

Links: source; test/example.

Variant of #Vec where values must belong to a specific class, determined by its getter cls. The default element class is Object. Override it when subclassing ClsVec. Elements added with .add are idempotently instantiated.

Also see #ClsColl.

class Person {
  constructor({name}) {this.name = name}
  pk() {return this.name}
}

class Persons extends c.ClsVec {
  get cls() {return Person}
}

const coll = new Persons()
  .add({name: `Mira`})
  .add({name: `Kara`})

console.log(coll)

/*
Persons {
  "$": [
    Person { name: "Mira" },
    Person { name: "Kara" },
  ]
}
*/

Undocumented

The following APIs are exported but undocumented. Check coll.mjs.