setmath
TypeScript icon, indicating that this package has built-in type declarations

0.1.1 • Public • Published

set ops

For changes see CHANGES.md

Set ops provides an enhanced javascript/typescript Set that includes some basic set operations and enhancements to make it easy and fun to use. By construction, this set can be used anywhere a regular JS/TS set is required.

To use the set, simply

npm install setmath

In your modules, import {SetWithOps, emptySet, setFrom} from 'setops' and then use the factory function to create the set

const setA = setFrom([1, 2, 3, 5])
const setB = setFrom([1, 3, 5, 7, 8])
const union = setA.union(setB)
if (setA.intersection(setB).nonEmpty()) {
    console.log("they intersect!")
}

console.log("In A but not in B", setA.compliment(setB))

Additional features for sets are:

  • map, filter
  • union, intersection, compliment, symmetric difference, cartesian product
  • is subset, is proper subset
  • empty set
  • equality

When constructing sets of objects or tuples, an optional comparator function can be supplied that is used for determining element equality. Operations with element equality will be a bit slower than operations on sets of primitive elements.

const comparator = (a: [number, string], b: [number, string]) => a[0] === b[0] && a[1] === b[1]
const C = setFrom<[number, string]>([[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd'], [5, 'e'], [6, 'f'], [7, 'g']], comparator)
const D = setFrom<[number, string]>([[2, 'b'], [3, 'c'], [4, 'd'], [5, 'e'], [6, 'f'], [7, 'g'], [10, 'ten']], comparator)

// true
C.has([1, 'a'])
// false
C.has([1, 'b'])
// true
C.delete([1, 'a'])
// false
C.has([1, 'a'])
// true
C.add([10, 'ten']).equals(D)

using setmath

The SetWithOps<T> returned from the setmath factory functions extends the Javascript Set object, adding useful operations and convenience methods. Consequently, it can be passed into any function that requires a type Set as an argument.

creation

// an empty set of numbers
import {emptySet} from "./sets";

const empty = emptySet<number>()

Creates an empty set of numbers.

// an empty set of Pigs
type Pig = {
    name: string
    age: number
}
const comparator = (a: Pig, b: Pig) => a.name === b.name && a.age === b.age

const pigs = emptySet<Pig>(comparator)

Creates an empty set of Pig objects. To ensure that pigs are compared by name and age rather than by the reference of the object, we must provide a comparator function when creating this set. Once the comparator function is provided, we can (mostly) conveniently forget about it.

Suppose we want to create a set that already contains Pig objects.

// using the Pig type and comparator from the previous code
const myPigs = setFrom([{name: 'wilbur', age: 8}, {name: 'stinky', age: 3}], comparator)

The setFrom(...) function returns an enhanced set. When creating sets from primitive types, you can leave off the comparator.

const pi = setFrom([3, 1, 4, 1, 5, 9])

The setFrom(...) accepts array, sets, and other SetWithOps objects from which it creates the set.

const pi1 = setFrom([3, 1, 4, 1, 5, 9])
const pi2 = setFrom(new Set([3, 1, 4, 1, 5, 9]))
const pi3 = setFrom(pi1)

queries

Once a set is created, we can ask questions about the set.

// using the Pig type and comparator from the previous code
const myPigs = setFrom([{name: 'wilbur', age: 8}, {name: 'stinky', age: 3}], comparator)
console.log(myPigs.cardinality, myPigs.size)
if (myPigs.nonEmpty()) {
    console.log("I've got pigs!")
}
if (myPigs.isEmpty()) {
    console.log("No pigs...so sad")
}

const yourPigs = setFrom([{name: 'snorter', age: 10}], comparator)

if (myPigs.intersection(yourPigs).nonEmpty()) {
    console.log("You've got my pigs!")
}
if (myPigs.isSubsetOf(yourPigs)) {
    console.log("I've got some of your pigs!")
}

manipulation

A year has passed, and we want to update the age of our pigs.

const myPigs = setFrom([{name: 'wilbur', age: 8}, {name: 'stinky', age: 3}], comparator)
const myAgedPigs = myPigs.map(pig => ({...pig, age: pig.age+1}))

And now, which pigs are younger than 5 years

const myYoungPigs = myPigs.filter(pig => pig.age < 5)
// only stinky

Or what is the average age of the pigs

const meanAge = myAgedPigs.reduce((sum, age) => sum += age) / myPigs.cardinality

We buy a new pig from you

const snorter = {name: 'snorter', age: 10}
myPigs.add(snorter)
yourPigs.delete(snorter)

And so you don't have any more pigs :(...

if (yourPigs.isEmpty()) {
    console.log("Fresh out of pigs!")
}

But, you may want to double check

console.log("Got snorter?", yourPigs.has(snorter) ? 'yep' : 'nope')

other set functions

const setA = setFrom([1, 2, 3, 4, 5])
const setB = setFrom([3, 4, 5, 6, 7, 8])

// what's in A that isn't in B?  
// [1, 2]
console.log(setA.compliment(setB).toArray())
// what's in A that isn't in B, and what's in B that isn't in A?  
// [1, 2, 6, 7, 8]
console.log(setA.symmetricDifference(setB).toArray())

Suppose we want to enumerate all the car models and colors. Things are a little more tricky. To ensure that each combination only gets added once, and that the new Set has the proper comparator, we need to create comparator for the resultant (color, model) tuples created by the cartesian product.

const colors = setFrom(['red', 'green', 'blue'])
const models = setFrom(['accord', 'prius', 'corsstrek'])

const comparator = ([colorA, modelA], [colorB, modelB]) => colorA === colorB && modelA === modelB
/*
[
  [ 'red', 'accord' ],
  [ 'red', 'prius' ],
  [ 'red', 'corsstrek' ],
  [ 'green', 'accord' ],
  [ 'green', 'prius' ],
  [ 'green', 'corsstrek' ],
  [ 'blue', 'accord' ],
  [ 'blue', 'prius' ],
  [ 'blue', 'corsstrek' ]
]
*/
console.log(colors.cartesianProduct(models, comparator))

Suppose you would like to enumerate all the combinations from a set of sets. Then the enumerateCombinations<T>(...sets: Array<SetWithOps<T>>): Array<SetWithOps<T>> function is what you're looking for. This function enumerates the combinations of the sets. For example, given the sets A = {a1}, B = {b1, b2}, and C = {c1, c2}. Then this function will generate [{a1, b1, c1}, {a1, b1, c1}, {a1, b2, c1}, {a1, b2, c2}].

const combos = enumerateCombinations(
    setFrom(['a1', 'a2']),
    setFrom(['b1', 'b2']),
    setFrom(['c1', 'c2']),
)
expect(combos).toEqual([
    setFrom(['a1', 'b1', 'c1']),
    setFrom(['a1', 'b1', 'c2']),
    setFrom(['a1', 'b2', 'c1']),
    setFrom(['a1', 'b2', 'c2']),
    setFrom(['a2', 'b1', 'c1']),
    setFrom(['a2', 'b1', 'c2']),
    setFrom(['a2', 'b2', 'c1']),
    setFrom(['a2', 'b2', 'c2']),
])

Readme

Keywords

none

Package Sidebar

Install

npm i setmath

Weekly Downloads

0

Version

0.1.1

License

MIT

Unpacked Size

118 kB

Total Files

21

Last publish

Collaborators

  • rob.philipp