can-set
can-set is a utility for comparing sets that
are represented by the parameters commonly passed to service requests.
For example, the set {type: "critical"}
might represent all
critical todos. It is a superset of the set {type: "critical", due: "today"}
which might represent all critical todos due today.
can-set is useful for building caching and other data-layer
optimizations. It can be used in client or server
environments. can-connect uses can-set to create data modeling
utilities and middleware for the client.
Play around in this JSBin!
- Install
- Use
- API
new set.Algebra(compares...)
compares Object<String,comparator()>
comparator(aValue, bValue, a, b, prop, algebra)
algebra.difference(a, b)
algebra.equal(a, b)
algebra.getSubset(a, b, bData)
algebra.getUnion(a, b, aItems, bItems)
algebra.index(set, items, item)
algebra.count(set)
algebra.has(set, props)
algebra.properSubset(a, b)
algebra.subset(a, b)
algebra.union(a, b)
can-set.comparators Object
new set.Translate(clauseType, propertyName)
- Contributing
Install
Use npm to install can-set
:
npm install can-set --save
Use
Use require
in Node/Browserify workflows to import can-set
like:
var set = require('can-set');
Use define
, require
or import
in StealJS workflows to import can-set
like:
import set from 'can-set'
Once you've imported set
into your project, use it to create a set.Algebra
and then
use that to compare and perform operations on sets.
var algebra = new set.Algebra(
set.comparators.id("_id"),
set.comparators.boolean("completed"),
set.comparators.rangeInclusive("start","end"),
set.comparators.sort("orderBy"),
)
algebra.subset({start: 2, end: 3}, {start: 1, end: 4})
algebra.difference({} , {completed: true})
algebra.getSubset({start: 2,end: 3},{start: 1,end: 4},
[{id: 1},{id: 2},{id: 3},{id: 4}])
Once you have the basics, you can use set algebra to all sorts of intelligent caching
and performance optimizations. The following example
defines a getTodos
function that gets todo data from a memory cache or from the server.
var algebra = new set.Algebra(
set.comparators.boolean("completed")
);
var cache = [];
var getTodos = function(set, cb) {
for(var i = 0 ; i < cache.length; i++) {
var cacheEntry = cache[i];
if(algebra.subset( set, cacheEntry.set ) ) {
var matchingTodos = algebra.getSubset(set, cacheEntry.set, cacheEntry.items)
return cb(matchingTodos);
}
}
$.get("/todos",set, function(todos){
cache.push({
set: set,
items: todos
});
cb(todos);
});
}
API
new set.Algebra(compares...)
Creates an object that can perform binary operations on sets with
an awareness of how certain properties represent the set.
var set = require("can-set");
var algebra = new set.Algebra(
set.comparators.boolean("completed"),
set.comparators.id("_id")
);
- compares
{compares}
:
Each argument is a compares. These
are returned by the functions on can-set.comparators or can be created
manually.
compares {Object\<String,comparator()\>}
An object of property names and comparator
functions.
{
lastName: function(aValue, bValue){
return (""+aValue).toLowerCase() === (""+bValue).toLowerCase();
}
}
comparator(aValue, bValue, a, b, prop, algebra)
A comparator function returns algebra values for two values for a given property.
- aValue
{*}
:
The value of A's property in a set difference A and B (A B). - bValue
{*}
:
The value of A's property in a set difference A and B (A B). - a
{*}
:
The A set in a set difference A and B (A B). - b
{*}
:
The B set in a set difference A and B (A B).
-
returns {Object|Boolean}
:
A comparator function should either return a Boolean which indicates if aValue
and bValue
are
equal or an AlgebraResult
object that details information about the union, intersection, and difference of aValue
and bValue
.
An AlgebraResult
object has the following values:
union
- A value the represents the union of A and B.intersection
- A value that represents the intersection of A and B.difference
- A value that represents all items in A that are not in B.count
- The count of the items in A.
For example, if you had a colors
property and A is ["Red","Blue"]
and B is ["Green","Yellow","Blue"]
, the
AlgebraResult object might look like:
{
union: ["Red","Blue","Green","Yellow"],
intersection: ["Blue"],
difference: ["Red"],
count: 2000
}
The count is 2000
because there might be 2000 items represented by colors "Red" and "Blue". Often
the real number can not be known.
algebra.difference(a, b)
Returns a set that represents the difference of sets A and B (A \ B), or
returns if a difference exists.
algebra1 = new set.Algebra(set.comparators.boolean("completed"));
algebra2 = new set.Algebra();
algebra1.difference( {} , {completed: true} )
algebra2.difference( {} , {completed: true} )
algebra2.difference( {completed: true}, {} )
- a
{can-set.set}
:
A set. - b
{can-set.set}
:
A set.
-
returns {can-set.set|Boolean}
:
If an object is returned, it is difference of sets A and B (A \ B).
If true
is returned, that means that B is a subset of A, but no set object
can be returned that represents that set.
If false
is returned, that means there is no difference or the sets are not comparable.
algebra.equal(a, b)
Returns true if the two sets the exact same.
algebra.equal({type: "critical"}, {type: "critical"})
- a
{can-set.set}
:
A set. - b
{can-set.set}
:
A set.
- returns
{Boolean}
:
True if the two sets are equal.
algebra.getSubset(a, b, bData)
Gets a
set's items given a super set b
and its items.
algebra.getSubset(
{type: "dog"},
{},
[{id: 1, type:"cat"},
{id: 2, type: "dog"},
{id: 3, type: "dog"},
{id: 4, type: "zebra"}]
)
- a
{can-set.set}
:
The set whose data will be returned. - b
{can-set.set}
:
A superset of set a
. - bData
{Array<Object>}
:
The data in set b
.
- returns
{Array<Object>}
:
The data in set a
.
algebra.getUnion(a, b, aItems, bItems)
Unifies items from set A and setB into a single array of items.
algebra = new set.Algebra(
set.comparators.rangeInclusive("start","end")
);
algebra.getUnion(
{start: 1,end: 2},
{start: 2,end: 4},
[{id: 1},{id: 2}],
[{id: 2},{id: 3},{id: 4}]);
- a
{can-set.set}
:
A set. - b
{can-set.set}
:
A set. - aItems
{Array<Object>}
:
Set a
's items. - bItems
{Array<Object>}
:
Set b
's items.
- returns
{Array<Object>}
:
Returns items in both set a
and set b
.
algebra.index(set, items, item)
Returns where item
should be inserted into items
which is represented by set
.
algebra = new set.Algebra(
set.comparators.sort("orderBy")
);
algebra.index(
{orderBy: "age"},
[{id: 1, age: 3},{id: 2, age: 5},{id: 3, age: 8},{id: 4, age: 10}],
{id: 6, age: 3}
)
The default sort property is what is specified by
can-set.comparators.id. This means if that if the sort property
is not specified, it will assume the set is sorted by the specified
id property.
- set
{can-set.set}
:
The set
that describes items
. - items
{Array<Object>}
:
An array of data objects. - item
{Object}
:
The data object to be inserted.
- returns
{Number}
:
The position to insert item
.
algebra.count(set)
Returns the number of items that might be loaded by the set
. This makes use of set.Algebra's
By default, this returns Infinity.
var algebra = new set.Algebra({
set.comparators.rangeInclusive("start", "end")
});
algebra.count({start: 10, end: 19})
algebra.count({})
- set
{can-set.set}
:
[description]
- returns
{Number}
:
The number of items in the set if known, Infinity
if unknown.
algebra.has(set, props)
Used to tell if the set
contains the instance object props
.
var algebra = new set.Algebra(
new set.Translate("where","$where")
);
algebra.has(
{"$where": {playerId: 5}},
{id: 5, type: "3pt", playerId: 5, gameId: 7}
) //-> true
- set
{can-set.set}
:
A set. - props
{Object}
:
An instance's raw data.
- returns
{Boolean}
:
Returns true
if props
belongs in set
and
false
it not.
algebra.properSubset(a, b)
Returns true if A is a strict subset of B (A ⊂ B).
algebra.properSubset({type: "critical"}, {})
algebra.properSubset({}, {})
- a
{can-set.set}
:
A set. - b
{can-set.set}
:
A set.
- returns
{Boolean}
:
true
if a
is a subset of b
and not equal to b
.
algebra.subset(a, b)
Returns true if A is a subset of B or A is equal to B (A ⊆ B).
algebra.subset({type: "critical"}, {})
algebra.subset({}, {})
- a
{can-set.set}
:
A set. - b
{can-set.set}
:
A set.
- returns
{Boolean}
:
true
if a
is a subset of b
.
algebra.union(a, b)
Returns a set that represents the union of A and B (A ∪ B).
algebra.union(
{start: 0, end: 99},
{start: 100, end: 199},
)
- a
{can-set.set}
:
A set. - b
{can-set.set}
:
A set.
-
returns {can-set.set|undefined}
:
If an object is returned, it is the union of A and B (A ∪ B).
If undefined
is returned, it means a union can't be created.
can-set.comparators {Object}
Contains a collection of comparator generating functions.
The following functions create compares
objects that can be mixed together to create a set Algebra
.
var algebra = new set.Algebra(
{
sessionId: function(){ return true }
},
set.comparators.boolean("completed"),
set.comparators.rangeInclusive("start","end")
);
set.comparators.boolean(property)
Makes a compare object with a property
function that has the following logic:
A(true) ∪ B(false) = undefined
A(undefined) \ B(true) = false
A(undefined) \ B(false) = true
It understands that true
and false
are complementary sets that combined to undefined
. Another way to think of this is that if you load {complete: false}
and {complete: true}
you've loaded {}
.
set.comparators.rangeInclusive(startIndexProperty, endIndexProperty)
Makes a comparator for two ranged properties that specify a range of items
that includes both the startIndex and endIndex. For example, a range of
[0,20] loads 21 items.
- startIndexProperty
{String}
:
The starting property name - endIndexProperty
{String}
:
The ending property name
- returns
{compares}
:
Returns a comparator
set.comparators.enum(property, propertyValues)
Makes a comparator for a set of values.
var compare = set.comparators.enum("type", ["new","accepted","pending","resolved"])
Defines the sortable property and behavior.
var algebra = new set.Algebra(set.comparators.sort("sortBy"));
algebra.index(
{sortBy: "name desc"},
[{name: "Meyer"}],
{name: "Adams"})
algebra.index(
{sortBy: "name"},
[{name: "Meyer"}],
{name: "Adams"})
- prop
{String}
:
The sortable property. - sortFunc
{function(sortPropValue, item1, item2)}
:
The
sortable behavior. The default behavior assumes the sort property value
looks like PROPERTY DIRECTION
(ex: name desc
).
- returns
{compares}
:
Returns a compares that can be used to create
a set.Algebra
.
set.comparators.id(prop)
Defines the property name on items that uniquely
identifies them. This is the default sorted property if no
can-set.comparators.sort is provided.
var algebra = new set.Algebra(set.comparators.id("_id"));
algebra.index(
{sortBy: "name desc"},
[{name: "Meyer"}],
{name: "Adams"})
algebra.index(
{sortBy: "name"},
[{name: "Meyer"}],
{name: "Adams"})
- prop
{String}
:
The property name that defines the unique property id.
- returns
{compares}
:
Returns a compares that can be used to create
a set.Algebra
.
new set.Translate(clauseType, propertyName)
Localizes a clause's properties within another nested property.
var algebra = new set.Algebra(
new set.Translate("where","$where")
);
algebra.has(
{$where: {complete: true}},
{id: 5, complete: true}
)
This is useful when filters (which are where
clauses) are
within a nested object.
- clause
{String}
:
A clause type. One of 'where'
, 'order'
, 'paginate'
, 'id'
. - propertyName
{String|Object}
:
The property name which contains the clauses's properties.
- returns
{set.compares}
:
A set compares object that can do the translation.
Contributing
To setup your dev environment:
- Clone and fork this repo.
- Run
npm install
. - Open
test.html
in your browser. Everything should pass. - Run
npm test
. Everything should pass. - Run
npm run-script build
. Everything should build ok.
To publish:
- Update the version number in package.json and commit and push this.
- Run
npm publish
. This should generate the dist folder. - Create and checkout a "release" branch.
- Run
git add -f dist
. - Commit the addition and tag it
git tag v0.2.0
. Push the tag git push origin --tags
.