From 4bfe2a860136f6310d18e44680329f85bf67dd74 Mon Sep 17 00:00:00 2001 From: Ivan S Glazunov Date: Sat, 23 Nov 2024 15:47:27 +0400 Subject: [PATCH] feat(deep): implement or operator and tests - Add or operator implementation in Deep class - Implement proper set merging for or operations - Add comprehensive test for or operator - Clean up error messages - Fix AnyGet method to return undefined for non-matches --- README.md | 18 +++++++++--------- src/deep.ts | 28 +++++++++++++++++----------- src/test.ts | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 35bbdb0..5666130 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ All methods work uniformly across different data types, treating single items as - Logical operators for filtering: - [x] `and` - Array of expressions that all must match - [x] `not` - Expression that must not match - - [ ] `or` - Array of expressions where at least one must match + - [x] `or` - Array of expressions where at least one must match - Condition types (comparison operators): - [ ] `eq` - Equal to - [ ] `neq` - Not equal to @@ -249,14 +249,14 @@ const B = deep.new(); const C = deep.new(); // Create instances -const a1 = deep.new(); -const b1 = deep.new(); -const c1 = deep.new(); - -// Set types for instances -a1.type = A; // a1 is of type A -b1.type = B; // b1 is of type B -c1.type = C; // c1 is of type C +const a1 = A.new(); +const b1 = B.new(); +const c1 = C.new(); + +// (If we create a1,b1,c1 from deep.new(), not from A,B,C.new()), type can be setted manually: +// a1.type = A; // a1 is of type A +// b1.type = B; // b1 is of type B +// c1.type = C; // c1 is of type C // Create relationships a1.from = b1; // a1 points from b1 diff --git a/src/deep.ts b/src/deep.ts index c2102d7..e7a428d 100644 --- a/src/deep.ts +++ b/src/deep.ts @@ -383,7 +383,7 @@ export class Deep { deep.contains.AnyHas = deep.contains.MethodHas.new((current, it) => current.call === it); _insert(deep.contains.Compatable, deep.contains.AnyHas, deep.contains.Any); - deep.contains.AnyGet = deep.contains.MethodGet.new((current, it) => current.call === it ? it : it); + deep.contains.AnyGet = deep.contains.MethodGet.new((current, it) => current.call === it ? it : undefined); _insert(deep.contains.Compatable, deep.contains.AnyGet, deep.contains.Any); deep.contains.AnySize = deep.contains.MethodSize.new((current) => 1); @@ -485,7 +485,7 @@ export class Deep { _insert(deep.contains.Compatable, deep.contains.SetAdd, deep.contains.Set); deep.contains.SetSet = deep.contains.MethodSet.new((current, key, value) => { - if (key !== value) throw new Error(`💁 Can't set into Set when key != value`); + if (key !== value) throw new Error(` Can't set into Set when key != value`); current.call.add(value); return current; }); @@ -924,7 +924,7 @@ export class Deep { return current; } else { for (let id of ids) return id.call; - throw new Error(`🤔 Unexpected, ids can't be empty here.`); + throw new Error(` Unexpected, ids can't be empty here.`); } } @@ -943,7 +943,7 @@ export class Deep { set value(value) { const previous = this.call; if (isUndefined(value)) { - if (isValue(this.value)) throw new Error(`🤦 If .value is Value, it's can't be ereised!`); + if (isValue(this.value)) throw new Error(` If .value is Value, it's can't be ereised!`); this.deep.memory.values.unset(this); } else if (isValue(value) && this.type == this.deep.Id) { this.deep.memory.values.unset(this); @@ -954,7 +954,7 @@ export class Deep { } else if (isDeep(value) && isValue(value.value)) { this.deep.memory.values.unset(this); this.deep.memory.values.set(this, value); - } else throw new Error('🙅 Value must be isValue(value) or isValue(value.value) or isUndefined(value)'); + } else throw new Error(' Value must be isValue(value) or isValue(value.value) or isUndefined(value)'); const current = this.value; if (previous !== current) { const event = this._createChangeEvent('change', 'value', previous, current); @@ -1195,8 +1195,8 @@ export class Deep { else { const valued = this.deep.memory.values.many(value); if (valued.size) { - if (!valued.size) throw new Error(`🤔 Unexpected, value can't be in all, but not values.`); - if (valued.size != 1) throw new Error(`🤔 Unexpected, value can only one Deep in .memory.values.many.`); + if (!valued.size) throw new Error(` Unexpected, value can't be in all, but not values.`); + if (valued.size != 1) throw new Error(` Unexpected, value can only one Deep in .memory.values.many.`); for (let v of valued) { return v; } @@ -1451,7 +1451,7 @@ export class Deep { * @returns Expanded deep.Exp */ exp(input: any, selection: Deep) { - if (isDeep(input) || (!isObject(input))) throw new Error(`🙅 Exp must be plain object or array for and operator`); + if (isDeep(input) || (!isObject(input))) throw new Error(` Exp must be plain object or array for and operator`); const exp: any = this.deep.Exp.new({}); if (isArray(input)) { @@ -1476,7 +1476,7 @@ export class Deep { this.exp(input[key], nestedSelection); exp.call[key] = nestedSelection; nestedSelection.on((e) => relation.emit(e)); - } else throw new Error(`🙅 Only Deep or plain objects Exp can be value in exp (${key})!`); + } else throw new Error(` Only Deep or plain objects Exp can be value in exp (${key})!`); relation.from = selection; relation.to = exp.call[key]; relation.on((e) => selection.emit(e)); @@ -1530,6 +1530,12 @@ export class Deep { set = arrayOfSets.reduce((result, set) => { return result.intersection(set.call); }, currentSet); + } else if (relation.typeof(this.deep.contains.or)) { + const arrayOfSets = relation.to.call(); + set = arrayOfSets.reduce((result, item) => { + const itemSet = item.call; + return result ? new Set([...result, ...itemSet]) : itemSet; + }, set); } } else if (relation.typeof(this.deep.Order)) { const nextSet = relation.to.call(); @@ -1764,8 +1770,8 @@ export class Contains { return founded; }, set(target, key, value, receiver) { - if (key === 'deep') throw new Error('🙅 Key "deep" is reserved in contains!'); - if (!isDeep(value)) throw new Error('🙅 Value must be Deep!'); + if (key === 'deep') throw new Error(' Key "deep" is reserved in contains!'); + if (!isDeep(value)) throw new Error(' Value must be Deep!'); let founded: Deep | void = undefined; const Contain = target.deep.deep.Contain; for (let contain of target.deep.out.call) { diff --git a/src/test.ts b/src/test.ts index 5d282ac..8944f23 100644 --- a/src/test.ts +++ b/src/test.ts @@ -280,6 +280,8 @@ test(`Symbol methods`, () => { assert.equal(d.has(Symbol()), false); assert.equal(d.get(value), value); + assert.equal(d.get(Symbol()), undefined); + assert.equal(d.size, 1); assert.deepEqual(d.map(v => v), [value]); assert.equal(d.add(value), value); @@ -302,6 +304,8 @@ test(`String methods`, () => { assert.equal(d.has('def'), false); assert.equal(d.get(value), value); + assert.equal(d.get('def'), undefined); + assert.equal(d.size, 3); assert.deepEqual(d.map(v => v), [value]); assert.equal(d.add(value), value); @@ -324,6 +328,8 @@ test(`Number methods`, () => { assert.equal(d.has(234), false); assert.equal(d.get(value), value); + assert.equal(d.get(234), undefined); + assert.equal(d.size, 3); assert.deepEqual(d.map(v => v), [value]); assert.equal(d.add(value), value); @@ -346,6 +352,8 @@ test(`BigInt methods`, () => { assert.equal(d.has(BigInt(234)), false); assert.equal(d.get(value), value); + assert.equal(d.get(BigInt(234)), undefined); + assert.equal(d.size, 3); assert.deepEqual(d.map(v => v), [value]); assert.equal(d.add(value), value); @@ -677,6 +685,31 @@ test('and operator', () => { }); }); +test('or operator', () => { + const deep = new Deep(); + + // Create test instances + const Type1 = deep.new(); + const Type2 = deep.new(); + const instance1 = Type1.new(); + const instance2 = Type1.new(); + const instance3 = Type1.new(); + + // Test or operator + const orQuery = deep.select({ + or: [ + { type: Type1 }, + { type: Type2 } + ] + }); + + const result = orQuery.call(); + assert(result.has(instance1)); + assert(result.has(instance2)); + assert(result.has(instance3)); + assert.equal(result.size, 3); +}); + test('association events order', () => { const deep = new Deep(); const events: DeepEvent[] = [];