Skip to content

Commit

Permalink
several additions: more polymorphism and support for set 💣
Browse files Browse the repository at this point in the history
- (fixes #4)
- (fixes #6)
- (fixes #8)
  • Loading branch information
ngarbezza committed Feb 24, 2019
1 parent 455382c commit 8b2cccd
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 50 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [asSet() for array and strings (#1)](https://github.com/ngarbezza/oow/issues/1)
- [occurrencesOf(object) for array and strings (#2)](https://github.com/ngarbezza/oow/issues/2)
- [includesAllOf(collection) for arrays and strings (#3)](https://github.com/ngarbezza/oow/issues/3)
- [Add existing messages to Set, which is a collection (#4)](https://github.com/ngarbezza/oow/issues/4)
- [forEach() for strings (#6)](https://github.com/ngarbezza/oow/issues/6)
- [equals() for strings (#8)](https://github.com/ngarbezza/oow/issues/8)
- Messages on Set to have more polymorphism: `filter`, `includes`, `equals`, `asSet`

## [1.5.1] - 2019-02-23

Expand Down
88 changes: 66 additions & 22 deletions oow.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,80 @@

(function () {
const Collection = {
isEmpty() { return this.length === 0; },
isEmpty() { return this.dimension() === 0; },
notEmpty() { return !this.isEmpty(); },
first() { return this[0]; },
second() { return this[1]; },
third() { return this[2]; },
last() { return this[this.length-1]; },
any(predicate) {
for (let elem in this)
if (predicate(this[elem])) return true;
return false;
let found = false;
this.forEach(elem => {
if (predicate(elem)) return found = true;
});
return found;
},
all(predicate) { return !this.any(elem => !predicate(elem)); },
includesAllOf(collection) { return collection.all(elem => this.includes(elem)); },
count(predicate) { return this.filter(predicate).dimension(); },
};

const SequenceableCollection = {
dimension() { return this.length; },
first() { return this[0]; },
second() { return this[1]; },
third() { return this[2]; },
last() { return this[this.dimension()-1]; },
take(n) { return this.slice(0, n); },
drop(n) { return this.slice(n, this.length); },
count(predicate) { return this.filter(predicate).length; },
drop(n) { return this.slice(n, this.dimension()); },
asSet() { return new Set(this); },
occurrencesOf(object) { return this.count(elem => elem === object); },
includesAllOf(collection) { return collection.all(elem => this.includes(elem)); },
};

const HeterogeneusCollection = {
compact() {
return this.filter(elem => elem !== null && elem !== undefined);
},
sum(func, startValue) {
let result = startValue || 0;
this.forEach(elem => result += (func && func(elem)) || elem);
return result;
},
};

const ArrayExtensions = {
equals(array) {
if (!array) return false;
let differentLength = this.length !== array.length;
let differentLength = this.dimension() !== array.dimension();
if (differentLength) return false;

for (let i = 0, l = this.length; i < l; i++) {
for (let i = 0, l = this.dimension(); i < l; i++) {
let elemsAreArrays = (this[i] instanceof Array) && (array[i] instanceof Array);
if (elemsAreArrays && !this[i].equals(array[i])) return false;
if (this[i] !== array[i]) return false
}
return true
},
compact() {
return this.filter(elem => elem !== null && elem !== undefined);
},
sum(func, startValue) {
let sumElement = (acc, elem) => acc + ((func && func(elem)) || elem);
return this.reduce(sumElement, startValue || 0);
},
};

const StringExtensions = {
filter(predicate) {
return this.split('').filter(predicate).join('');
}
},
forEach(func) {
for (let index in this) func(this[index]);
},
equals(string) { return this === string; },
};

const SetExtensions = {
dimension() { return this.size; },
includes(element) { return this.has(element); },
equals(set) {
return this.includesAllOf(set) && set.includesAllOf(this);
},
filter(predicate) {
let result = new Set();
this.forEach(elem => { if (predicate(elem)) result.add(elem) });
return result;
},
asSet() { return this; }
};

let eachExtensionOf = (extension, block) =>
Expand All @@ -57,16 +85,32 @@
Object.defineProperty(proto, methodName, { value: extension[methodName] });

eachExtensionOf(Collection, methodName =>
[Array.prototype, String.prototype].forEach(proto =>
[Array.prototype, String.prototype, Set.prototype].forEach(proto =>
extend(proto, Collection, methodName)
)
);

eachExtensionOf(SequenceableCollection, methodName =>
[Array.prototype, String.prototype].forEach(proto =>
extend(proto, SequenceableCollection, methodName)
)
);

eachExtensionOf(HeterogeneusCollection, methodName =>
[Array.prototype, Set.prototype].forEach(proto =>
extend(proto, HeterogeneusCollection, methodName)
)
);

eachExtensionOf(ArrayExtensions, methodName =>
extend(Array.prototype, ArrayExtensions, methodName)
);

eachExtensionOf(StringExtensions, methodName =>
extend(String.prototype, StringExtensions, methodName)
);

eachExtensionOf(SetExtensions, methodName =>
extend(Set.prototype, SetExtensions, methodName)
);
})();
151 changes: 123 additions & 28 deletions tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,31 @@
require('./oow');
const { suite, test, assert } = require('@pmoo/testy');

suite('messages added to array and string', () => {
const identity = elem => elem;

suite('messages added to Array, String and Set', () => {
// isEmpty
test('list is empty', () => assert.isTrue([].isEmpty()));
test('string is empty', () => assert.isTrue(''.isEmpty()));
test('isEmpty is true for array/set/string', () => {
assert.isTrue([].isEmpty());
assert.isTrue(''.isEmpty());
assert.isTrue(new Set().isEmpty());
});
test('isEmpty is false for array/set/string', () => {
assert.isFalse([1].isEmpty());
assert.isFalse('a'.isEmpty());
assert.isFalse(new Set([4]).isEmpty());
});
// notEmpty
test('list is not empty', () => assert.isTrue([1].notEmpty()));
test('string is not empty', () => assert.isTrue('a'.notEmpty()));
test('notEmpty is true for array/set/string', () => {
assert.isTrue([1].notEmpty());
assert.isTrue('a'.notEmpty());
assert.isTrue(new Set([4]).notEmpty());
});
test('notEmpty is false for array/set/string', () => {
assert.isFalse([].notEmpty());
assert.isFalse(''.notEmpty());
assert.isFalse(new Set().notEmpty());
});
// first
test('first of list', () => assert.areEqual([1, 2, 3].first(), 1));
test('first of string', () => assert.areEqual('hola'.first(), 'h'));
Expand All @@ -23,38 +41,85 @@ suite('messages added to array and string', () => {
test('last of list', () => assert.areEqual([1, 2, 3].last(), 3));
test('last of string', () => assert.areEqual('hola'.last(), 'a'));
// any
test('any in case true - lists', () => assert.isTrue([1,2,3].any(num => num > 2)));
test('any in case true - string', () => assert.isTrue('hola'.any(letter => letter === 'o')));
test('any in case false - lists', () => assert.isFalse([1,2,3].any(num => num > 5)));
test('any in case false - lists', () => assert.isFalse('hola'.any(letter => letter === 'w')));
test('any in case empty', () => assert.isFalse([].any(_ => false)));
test('any in case true for array/string/set',() => {
assert.isTrue([1, 2, 3].any(num => num > 2));
assert.isTrue('hola'.any(letter => letter === 'o'));
assert.isTrue(new Set([1, 2, 3]).any(num => num < 2));
});
test('any in case false for array/string/set', () => {
assert.isFalse([1, 2, 3].any(num => num > 5));
assert.isFalse([].any(_ => false));
assert.isFalse('hola'.any(letter => letter === 'w'));
assert.isFalse(new Set([1, 2, 3]).any(num => num < -1));
});
// all
test('all in case true - lists', () => assert.isTrue([1,2,3].all(num => num < 4)));
test('all in case true - string', () => assert.isTrue('hola'.all(letter => letter.match(/[a-z]/i))));
test('all in case false - lists', () => assert.isFalse([1,2,3].all(num => num > 2)));
test('all in case false - lists', () => assert.isFalse('aaah'.all(letter => letter === 'a')));
test('all in case empty', () => assert.isTrue([].all(_ => false)));
test('all in case true for array/set/string', () => {
assert.isTrue([].all(_ => false));
assert.isTrue([1, 2, 3].all(num => num < 4));
assert.isTrue('hola'.all(letter => letter.match(/[a-z]/i)));
assert.isTrue(new Set([3, 4, 5]).all(num => num >= 3));
});
test('all in case false for array/set/string', () => {
assert.isFalse([1, 2, 3].all(num => num > 2));
assert.isFalse('aaah'.all(letter => letter === 'a'));
assert.isFalse(new Set([9, 7, 5]).all(num => num > 7));
});
// equals
test('arrays are equal if they have the same elements in the same order', () => {
assert.areEqual([], [], 'equals');
assert.areEqual([1, 2, 3], [1, 2, 3], 'equals');
});
test('arrays are not equal if they have different elements or different elements order', () => {
assert.areNotEqual([1, 2], [1], 'equals');
assert.areNotEqual([1, 2, 3], [1, 3, 2], 'equals');
});
test('strings are equal if are ===', () => assert.areEqual('hola', 'hola', 'equals'));
test('strings are not equal if they are !==', () => assert.areNotEqual('hola', 'halo', 'equals'));
test('sets are equal if they have the same elements', () => {
assert.areEqual(new Set([]), new Set([]), 'equals');
assert.areEqual(new Set([1, 2, 3, 3]), new Set([3, 2, 1]), 'equals');
});
test('sets are not equal if they have different elements', () => {
assert.areNotEqual(new Set([1, 2]), new Set([1]), 'equals');
assert.areNotEqual(new Set([1, 2]), new Set([1, 2, 3]), 'equals');
});
// take
test('take 2 on a list', () => assert.areEqual([3,2,1].take(2), [3,2]));
test('take 2 on a string', () => assert.areEqual('hola'.take(2), 'ho'));
// drop
test('drop 2 on a list', () => assert.areEqual([3,2,1].drop(2), [1]));
test('drop 2 on a string', () => assert.areEqual('hola'.drop(2), 'la'));
// compact
test('compact a list with nulls', () => assert.areEqual([1, null, 2].compact(), [1,2]));
test('compact a list with undefineds', () => assert.areEqual([undefined, 1, undefined, 2].compact(), [1,2]));
test('compact a list/set with nulls', () => {
assert.areEqual([1, null, 2].compact(), [1, 2]);
assert.areEqual(new Set([1, null, 2]).compact(), new Set([1, 2]));
});
test('compact a list/set with undefineds', () => {
assert.areEqual([undefined, 1, undefined, 2].compact(), [1,2])
});
// count
test('counting on list', () => assert.areEqual([1, 42, 2, 23].count(n => n >= 2), 3));
test('counting on string', () => assert.areEqual('holaaaaa'.count(l => l === 'a'), 5));
test('counting on string', () => assert.areEqual('holaaaaa'.count(l => l === 'a'), 5));
test('counting on string', () => assert.areEqual('holaaaaa'.count(l => l === 'a'), 5));
// filter on string
test('filtering on string', () => assert.areEqual('asaoamaeatahaianaga'.filter(l => l !== 'a'), 'something'));
test('counting (present elements) on list/string/set', () => {
assert.areEqual([1, 42, 2, 23].count(n => n >= 2), 3);
assert.areEqual('holaaaaa'.count(l => l === 'a'), 5);
assert.areEqual(new Set([2, 4, 7]).count(n => n % 2 === 0), 2);
});
test('counting (no elements) on array/string/set', () => {
assert.areEqual([1, 42, 2, 23].count(n => n === 0), 0);
assert.areEqual('holaaaaa'.count(l => l === 'z'), 0);
assert.areEqual(new Set([2, 4, 7]).count(n => n % 8 === 0), 0);
});
// filter on string/set
test('filtering on string/set', () => {
let notAnA = l => l !== 'a';
assert.areEqual('asaoamaeatahaianaga'.filter(notAnA), 'something');
assert.areEqual(new Set('asaoamaeatahaianaga').filter(notAnA), new Set('something'));
});
// asSet
test('asSet on list', () => assert.areEqual([1, 42, 1, 1].asSet(), new Set([1, 42])));
test('asSet on string', () => assert.areEqual('holaaaaa'.asSet(), new Set('hola')));
test('asSet on array', () => assert.areEqual([1, 42, 1, 1].asSet(), new Set([1, 42])));
test('asSet on string', () => assert.areEqual('holaaaaa'.asSet(), new Set('hola')));
test('asSet on set', () => assert.areEqual(new Set('hola').asSet(), new Set('hola')));
// occurrences of element
test('occurrences of object on list', () => {
test('occurrences of object on array', () => {
assert.areEqual([1, 1, 1, 23].occurrencesOf(1), 3);
assert.areEqual([1, 1, 1, 23].occurrencesOf(23), 1);
assert.areEqual([1, 1, 1, 23].occurrencesOf(100), 0);
Expand All @@ -68,12 +133,42 @@ suite('messages added to array and string', () => {
assert.areEqual('holaaaaa'.occurrencesOf('z'), 0);
});
// includes "all of"
test('includes all of an array/string', () => {
test('includes all of an array/string/set', () => {
assert.isTrue([1, 2, 3].includesAllOf([2, 3]));
assert.isTrue('holaaaaa'.includesAllOf('alo'));
assert.isTrue(new Set('holaaaaa').includesAllOf(new Set('alo')));
});
test('does not include all of an array/string', () => {
test('does not include all of an array/string/set', () => {
assert.isFalse([1, 2, 3].includesAllOf([2, 4]));
assert.isFalse('holaaaaa'.includesAllOf('alou'));
assert.isFalse(new Set('holaaaaa').includesAllOf(new Set('alou')));
});
// forEach for strings
test('forEach for strings', () => {
const elems = [];
'hola'.forEach(letter => elems.push(letter));
assert.areEqual(elems, ['h', 'o', 'l', 'a']);
});
// includes for sets
test('set includes an element', () => assert.isTrue(new Set([2, 1]).includes(1)));
test('set does not include an element', () => assert.isFalse(new Set([2, 1]).includes(3)));
// dimension (because length and size cannot be used)
test('dimension as a polymorphic message to get the size of array/set/string', () =>{
assert.areEqual([1, 2, 3].dimension(), 3);
assert.areEqual('hola'.dimension(), 4);
assert.areEqual(new Set('abcde').dimension(), 5);
});
// sum
test('sum elements of array/set with default starting value (0)', () => {
assert.areEqual([4, 5, 6].sum(), 15);
assert.areEqual(new Set([4, 5, 5]).sum(), 9);
});
test('sum elements of array/set with a function applied to each element', () => {
assert.areEqual([4, 5, 6].sum(elem => elem + 1), 18);
assert.areEqual(new Set([4, 5, 5]).sum(elem => elem - 1), 7);
});
test('sum elements of array/set with a starting value', () => {
assert.areEqual([1, 2, 3].sum(identity, 10), 16);
assert.areEqual(new Set([4, 5, 5]).sum(identity, 5), 14);
});
}).run();

0 comments on commit 8b2cccd

Please sign in to comment.