Skip to content

Commit

Permalink
- added equals method
Browse files Browse the repository at this point in the history
- more consistent usage of let & const
- updated readme
- updated basic usage example
  • Loading branch information
unnoon committed Aug 24, 2016
1 parent 383db71 commit 3b32891
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 35 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Fast & consistent JavaScript MultiSet (AKA bag, bunch) implementation. Implement
- Fast!
- Fully tested
- Fully documented
- Both bitset & bitvector like operands
- Pascal case operands can be used to output new multisets
- Chaining
- Lots of aliases
Expand All @@ -35,11 +34,36 @@ Fast & consistent JavaScript MultiSet (AKA bag, bunch) implementation. Implement
## Usage

```js
/* Coming soon.... */
var obj = {iam: 'object'};

var ms1 = MultiSet.create([7, 67, 7, 7, 'text', obj, 'text', obj]); // use an iterable for initialization.
var ms2 = Object.create(MultiSet).init([7, 67, 7, 'text', obj, 'text2', obj, 99]);
var ms3 = new MultiSet.constructor([7, 67, 7, 7, 'text', obj, 'text', obj]); // or use the constructor function. Preferably you would like to use the CMultiSet export for this.

expect(ms1.size).to.eql(8); // cardinality (@alias) of the multiset.
expect(ms1.toString()).to.eql('{7 => 3, 67 => 1, text => 2, [object Object] => 2}');
expect(ms1.has(7)).to.be.true;
expect(ms1.multiplicity(obj)).to.eql(2);
expect(ms1.multiplicity(undefined)).to.eql(0); // non existent elements have multiplicity 0.
expect(ms1.equals(ms3)).to.be.true; // checks equality between 2 multisets.

ms1.union(ms2);

expect(ms1.toString()).to.eql('{7 => 3, 67 => 1, text => 2, [object Object] => 2, text2 => 1, 99 => 1}');
expect(ms1.size).to.eql(10);

var ms4 = ms3.Union(ms2); // use Pascal case Union to output a new MultiSet and leave ms3 unchanged.

expect(ms3.toString(1)).to.eql('[7, 7, 7, 67, text, text, [object Object], [object Object]]'); // use mode=1 to output single dimension array-like string.
expect(ms4.toString()).to.eql('{7 => 3, 67 => 1, text => 2, [object Object] => 2, text2 => 1, 99 => 1}');
```

For more usage example see the unit tests @ /test/unit/MultiSet-spec.js

## Prototypal(OLOO) vs JS inheritance

By default prototypal(OLOO) inheritance is supported. Practically this means that the default export will be the MultiSet prototype. Also all static properties will be directly available on the prototype (as well as on the constructor function). If you prefer 'classical' inheritance a CMultiSet export is also provided.

## Documentation

Documentation can be generated by running the command below and is outputted @ /doc.
Expand Down
63 changes: 39 additions & 24 deletions src/MultiSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ const properties = {
*/
Difference: function(multiset)
{
var output = MultiSet.create();
let nmul;
const output = MultiSet.create();
let nmul;

multiset.elements.forEach((multiplicity, elm) => {
nmul = this.multiplicity(elm) - multiplicity;
Expand Down Expand Up @@ -210,10 +210,11 @@ const properties = {
each$: function(cb, ctx=null) {
"@aliases: forEach$, eachAll, forEachAll";
{
let count = 0;
let a, i, mul, count = 0;

// for(let [elm, multiplicity] of this.elements) // destructuring is nice but slow...
for(let a of this.elements)
{ for(let i = 0, mul = a[1]/*multiplicity*/; i < mul; i++, count++)
for(a of this.elements)
{ for(i = 0, mul = a[1]/*multiplicity*/; i < mul; i++, count++)
{
if(cb.call(ctx, a[0]/*elm*/, count, this) === false) {return false}
}
Expand All @@ -232,6 +233,22 @@ const properties = {
{
return this.elements.entries();
},
/**
* @method MultiSet#equals
* @desc
* Checks equality between 2 multisets.
*
* @param {MultiSet} multiset - Multiset to check equality with.
*
* @returns {boolean} Boolean indicating if the 2 multisets are equal.
*/
equals: function(multiset)
{ if(this.size !== multiset.size) {return false}

return multiset.each((mul, elm) => {
return mul !== this.multiplicity(elm)
});
},
/**
* @method MultiSet#has
* @desc **aliases:** isMember
Expand Down Expand Up @@ -301,8 +318,8 @@ const properties = {
Intersection: function(multiset) {
"@aliases: And";
{
let output = MultiSet.create();
let nmul;
const output = MultiSet.create();
let nmul;

this.elements.forEach((multiplicity, elm) => {
nmul = Math.min(multiplicity, multiset.multiplicity(elm));
Expand Down Expand Up @@ -416,8 +433,8 @@ const properties = {
SymmetricDifference: function(multiset) {
"@aliases: Exclusion";
{
let output = this.clone();
let diff;
const output = this.clone();
let diff;

multiset.elements.forEach((multiplicity, elm) => {
diff = output.multiplicity(elm) - multiplicity;
Expand All @@ -444,7 +461,7 @@ const properties = {
toString: function(mode) {
"@aliases: stringify";
{
var out = '';
let out = '';

switch(mode)
{
Expand Down Expand Up @@ -487,7 +504,7 @@ const properties = {
Union: function(multiset) {
"@aliases: Or";
{
let output = this.clone();
const output = this.clone();

multiset.elements.forEach((multiplicity, elm) => output.elements.set(elm, Math.max(multiplicity, output.multiplicity(elm))));

Expand All @@ -502,7 +519,7 @@ const properties = {
*/
values: function()
{
let data = [];
const data = [];

this.each$(val => data.push(val));

Expand All @@ -520,20 +537,20 @@ const properties = {
{
return this.elements.entries();
},
/**
* @name MultiSet#[@@toStringTag]
* @type string
* @desc
* Custom name for Object.prototype.toString.call(multiset) === [object MultiSet]
*/
["@@toStringTag"]: 'MultiSet',
/**
* @name MultiSet.[@@species]
* @type function
* @desc
* the species of the MultiSet. Which is just the MultiSet constructor.
*/
["static @@species"]: MultiSet
["static @@species"]: MultiSet,
/**
* @name MultiSet#[@@toStringTag]
* @type string
* @desc
* Custom name for Object.prototype.toString.call(multiset) === [object MultiSet]
*/
["@@toStringTag"]: 'MultiSet'
};
/**
* @constructor MultiSet
Expand Down Expand Up @@ -564,9 +581,7 @@ extend(MultiSet, properties);
*/
function extend(obj, properties)
{
for(let prop in properties)
{ if(!properties.hasOwnProperty(prop)) {continue}

Object.keys(properties).forEach(prop => {
let dsc = Object.getOwnPropertyDescriptor(properties, prop);
let attrs = prop.match(/[\w\$\@]+/g); prop = attrs.pop();
let aliases = `${dsc.value || dsc.get || dsc.set}`.match(/@aliases:(.*?);/);
Expand All @@ -578,7 +593,7 @@ function extend(obj, properties)
if(~attrs.indexOf('static')) {addProp(obj, name)}
addProp(obj.prototype, name);
});
}
});

return obj
}
Expand Down
49 changes: 40 additions & 9 deletions test/unit/MultiSet-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,28 @@ define([
describe("basic usage", function() {

it("should demonstrate the basic functions of cell-multiset", function() {
var obj = {iam: 'object'};

var ms = MultiSet.spawn([7, 67, 23]);
var ms1 = MultiSet.create([7, 67, 7, 7, 'text', obj, 'text', obj]); // use an iterable for initialization.
var ms2 = Object.create(MultiSet).init([7, 67, 7, 'text', obj, 'text2', obj, 99]);
var ms3 = new MultiSet.constructor([7, 67, 7, 7, 'text', obj, 'text', obj]); // or use the constructor function. Preferably you would like to use the CMultiSet export for this.

expect(ms.has(7)).to.be.true;
expect(ms.has(67)).to.be.true;
expect(ms.has(23)).to.be.true;
expect(ms1.size).to.eql(8); // cardinality (@alias) of the multiset.
expect(ms1.toString()).to.eql('{7 => 3, 67 => 1, text => 2, [object Object] => 2}');
expect(ms1.has(7)).to.be.true;
expect(ms1.multiplicity(obj)).to.eql(2);
expect(ms1.multiplicity(undefined)).to.eql(0); // non existent elements have multiplicity 0.
expect(ms1.equals(ms3)).to.be.true; // checks equality between 2 multisets.

ms1.union(ms2);

expect(ms1.toString()).to.eql('{7 => 3, 67 => 1, text => 2, [object Object] => 2, text2 => 1, 99 => 1}');
expect(ms1.size).to.eql(10);

var ms4 = ms3.Union(ms2); // use Pascal case Union to output a new MultiSet and leave ms3 unchanged.

expect(ms3.toString(1)).to.eql('[7, 7, 7, 67, text, text, [object Object], [object Object]]'); // use mode=1 to output single dimension array-like string.
expect(ms4.toString()).to.eql('{7 => 3, 67 => 1, text => 2, [object Object] => 2, text2 => 1, 99 => 1}');
});
});

Expand Down Expand Up @@ -251,6 +267,21 @@ define([
});
});

describe("equals", function() {

it("should check equality between 2 multisets", function() {

var ms1 = MultiSet.create([7, 7, 67, 23]);
var ms2 = MultiSet.create([7, 7, 67, 23]);
var ms3 = MultiSet.create([7, 7, 7, 67, 23]);

expect(ms1 == ms2).to.be.false;
expect(ms1 === ms2).to.be.false;
expect(ms1.equals(ms2)).to.be.true;
expect(ms1.equals(ms3)).to.be.false;
});
});

describe("entries", function() {

it("should output an iterable object containing value & multiplicity", function() {
Expand Down Expand Up @@ -388,8 +419,8 @@ define([

it("should calculate the symmetricDifference between 2 multisets", function() {

var ms1 = MultiSet.create([1, 1]);
var ms2 = MultiSet.create([1, 2]);
var ms1 = MultiSet.create([1, 1, 3, 3]);
var ms2 = MultiSet.create([1, 2, 3, 3]);

ms1.symmetricDifference(ms2);

Expand All @@ -411,13 +442,13 @@ define([

it("should calculate the SymmetricDifference between 2 multisets", function() {

var ms1 = MultiSet.create([1, 1]);
var ms2 = MultiSet.create([1, 2]);
var ms1 = MultiSet.create([1, 1, 3, 3]);
var ms2 = MultiSet.create([1, 2, 3, 3]);

var sd = ms1.SymmetricDifference(ms2);

expect(sd.toString(1)).to.eql('[1, 2]');
expect(ms1.toString(1)).to.eql('[1, 1]');
expect(ms1.toString(1)).to.eql('[1, 1, 3, 3]');
});

it("should be possible to use the alias Exclusion", function() {
Expand Down

0 comments on commit 3b32891

Please sign in to comment.