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

@ocelotjungle/collections

v1.0.1

Published

Enhanced collections

Downloads

2

Readme

@ocelotjungle/collections

This package contains 7 collections that are subtypes of a standard Set and Map, and one complex collection DataPond, which can be used to manage some micro-lakes of data, relating entities to each other.


Collections

ComplexSet<K - key, I - item>

This collection acts like a common Set except ComplexSet allows to use object values. It compares equality not of items, but of something returned by selector (keys). Actually, this is just a map with selector. Creating a ComplexSet with item type { foo: number, bar: string } and key type number. First argument is a selector, that selects foo from item type. In this case, foo is a key and it will refleсt equality of items, so if a.foo == b.foo, then a == b. If updateExisting is true, then adding an item with already existing key will replace the existing item.

new ComplexSet<number, { foo: number, bar: string }>(({ foo }) => foo, [updateExisting])
  .add({ foo: 42, bar: "question" })
  .add({ foo: 42, bar: "not question" }) // replaces previous if updateExisting
  .add({ foo: 100, bar: "IOO" })

EnsuredMap<K - key, V - value>

Does nothing except get returns X instead of X | undefined. No real checking, no safety, only types. Use it only when you are absolutely sure here is a value for key.

new EnsuredMap<number, string>([entries])
  .set(42, "question") // { 42 => "question" }
  .get(42) // "question"
  .get(43) // undefined as V

ArrayEnsuredMap<K - key, V - value>

The same as EnsuredMap, but stores V[]. Also allows to push/clear array by a key. Other array functions are not implemented.

new ArrayEnsuredMap<number, string>([entries])
  .set(42, []) // { 42 => [] }
  .push(42, "question") // { 42 => ["question"] }
  .clear(42) // { 42 => [] }
  .clear() // { }

StrongMap<K - key, V - value>

This collection is a map that guarantees an existence of required key by using a callback that creates some default V.

new StrongMap<number, string>(() => "default value", [entries])
  .set(42, "question") // { 42 => "question" }
  .get(42) // "question"
  .get(43) // "default value"

ArrayStrongMap<K - key, V - value>

The same as StrongMap, but stores V[] and default value is always an empty array. Also allows to push/clear array by a key. Other array functions are not implemented.

new ArrayStrongMap<number, string>([entries])
  .push(42, "foo") // { 42 => ["foo"] }
  .get(42) // ["foo"]
  .get(43) // []

ComplexKeyMap<K - key, V - value>

This collection is a Map that allows to correctly use complex objects. When you use get, it won't compare object's reference equality, but its structure. Also, likely StrongMap, guarantees that get returns a defined value. Requires passing default value callback.

new ComplexKeyMap<{ foo: number, bar: string }, boolean>(() => false, [entries])
  .set({ foo: 42, bar: "question" }, true) // { { foo: 42, bar: "question" } => true }
  .get({ foo: 42, bar: "question" }) // true
  .get(43) // false

ArrayComplexKeyMap<K - key, V - value>

The same as ComplexKeyMap, but stores V[] and default value is always an empty array. Also allows to push/clear array by a key. Other array functions are not implemented.

new ArrayComplexKeyMap<{ foo: number, bar: string }, boolean>([entries])
  .push({ foo: 42, bar: "question" }, true) // { { foo: 42, bar: "question" } => [true] }
  .get({ foo: 42, bar: "question" }) // [true]
  .get({ foo: 43, bar: "no value" }) // []

DataPond<P - pond scheme>

This collection is like a micro-database with preconfigured JOINs. It realizes a crude state machine on types for user's convenience (that makes impossible to correctly extend this class, preserving the state machine).

Creating

DataPond.create<P>(idExtractors)
  • P is a scheme of data, it looks like { [name]: object }
  • idExtractors is an object like { [name]: extractor }
  • extractor is a selector function that accepts corresponding type object and returns some identifier

Example:

DataPond.create<{ foo: { bar: number, baz: Date } }>({ foo: ({ bar }) => bar })
  • foo is a pond entity, it's type { bar: number, baz: Date }
  • ({ bar }) => bar is an extractor, it selects bar from foo

Using

Add entities (add(name, entity))

Initially the pond has state 0 (next DataPond<0>), that means no cache. Adding one entity returns DataPond<1>, that means there's owner in the cache. Adding one more entity returns DataPond<2>, that means there're owner and item in the cache. Adding more entities won't affect state of the pond. The pond "remembers" only last 2 added entities.

Relate entities (relate(item, owner) or relate({ owner, item }))

owner and item can be either pointers to the entity ([entity_name, entity_id]) or arrays of pointers or even undefined, if applicable (depending on cache size).

DataPond<0>.relate:

  • requires both owner and item;
  • returns DataPond<0>;

DataPond<1>.relate:

  • requires either:
    • passing item in relate(item, owner) and in this case owner is optional (if pass no owner, will be used owner from cache)
    • or passing owner or item in relate({ owner, item }) and in this case the second argument will be optional (will be used from cache);
  • returns DataPond<1>, that allows add one entity and then call multiple relate in chain with different arguments;

DataPond<2>.relate:

  • requires no arguments and if called without them, will use owner and item from cache;
  • passing one or both arguments will lead to using them instead of cache;
  • returns DataPond<0>;
Fetching entities (fetch(name, id) or fetch(name, id, array_of_names))

fetch(name, id) returns entity with corresponding name and id. fetch(name, id, array_of_names) acts like fetch(name, id), but augments the result with related entities, which names are in array_of_names: { _[name]: related_entity[] }

Examples

type Foo = { id: number, value: string };
type Bar = { name: string };
type Schema = { foo: Foo, bar: Bar };

// Creating
const dp = new DataPond<Schema>({
    foo: ({ id }) => id,
    bar: ({ name }) => name,
});

// Adding

dp.add("foo", { id: 42, value: "question" });
dp.add("bar", { name: "baz" });
dp.relate(["bar", "baz"], ["foo", 42]); // relating item to owner, foo-42 owns bar-baz

dp
    .add("foo", { id: 43, value: "not question" }) // state 1, owner in cache
    .relate(["bar", "baz"]) // relates item to cached owner, foo-43 also owns bar-baz

dp
    .add("foo", { id: 44, value: "idk" }) // state 1, owner in cache
    .add("bar", { name: "buzz" }) // state 2, owner and item in cache
    .relate() // relates cached item to cached owner, foo-44 owns bar-buzz

// Fetching

dp.fetch("foo", 42) // { id: 42, value: "question" }
dp.fetch("foo", 44, ["bar"]) // { id: 44, value: "idk", _bar: [{ name: "buzz" }] }