Skip to content

Commit

Permalink
feat(json-expression): 🎸 implement object "o.del" operator
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jun 17, 2024
1 parent b676b55 commit 20c7aff
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 2 deletions.
18 changes: 18 additions & 0 deletions src/json-expression/__tests__/jsonExpressionUnitTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2034,6 +2034,24 @@ export const jsonExpressionUnitTests = (
})).toThrow(new Error('PROTO_KEY'));
});
});

describe('o.del', () => {
test('can delete an object property', () => {
check(['o.del', {foo: 'bar', baz: 'qux'}, 'foo', 'bar'], {baz: 'qux'});
});

test('object can be an expression', () => {
check(['o.del', ['$', ''], 'a', 'c', 'd'], {b: 2}, {a: 1, b: 2, c: 3});
});

test('prop can be an expression', () => {
check(['o.del', {a: 1, b: 2, c: 3}, ['$', '']], {a: 1, c: 3}, 'b');
});

test('object and prop can be an expression', () => {
check(['o.del', ['$', '/o'], ['$', '/p']], {a: 1, c: 3}, {o: {a: 1, b: 2, c: 3}, p: 'b'});
});
});
});

describe('Branching operators', () => {
Expand Down
50 changes: 49 additions & 1 deletion src/json-expression/operators/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import type * as types from '../types';

const validateSetOperandCount = (count: number) => {
if (count < 3) {
throw new Error('Not enough operands for "o.set" operand.');
throw new Error('Not enough operands for "o.set".');
}
if (count % 2 !== 0) {
throw new Error('Invalid number of operands for "o.set" operand.');
}
};

const validateDelOperandCount = (count: number) => {
if (count < 3) {
throw new Error('Not enough operands for "o.del".');
}
};

export const objectOperators: types.OperatorDefinition<any>[] = [
[
'keys',
Expand Down Expand Up @@ -119,4 +125,46 @@ export const objectOperators: types.OperatorDefinition<any>[] = [
return curr;
},
] as types.OperatorDefinition<types.ExprObjectSet>,

[
'o.del',
[],
-1,
/**
* Delete one or more properties from an object.
*
* ```
* ['o.del', {}, 'prop1', 'prop2']
* ```
*/
(expr: types.ExprObjectSet, ctx) => {
let i = 1;
const length = expr.length;
validateDelOperandCount(length);
let doc = util.asObj(ctx.eval(expr[i++], ctx)) as Record<string, unknown>;
while (i < length) {
const prop = util.str(expr[i++]) as string;
delete doc[prop];
}
return doc;
},
(ctx: types.OperatorCodegenCtx<types.ExprObjectSet>): ExpressionResult => {
const length = ctx.operands.length;
validateDelOperandCount(length + 1);
let i = 0;
let curr = ctx.operands[i++];
ctx.link(util.str, 'str');
ctx.link(util.objDelRaw, 'objDelRaw');
while (i < length) {
let prop = ctx.operands[i++];
if (prop instanceof Literal) {
prop = new Literal(util.str(prop.val));
} else if (prop instanceof Expression) {
prop = new Expression(`str(${prop})`);
}
curr = new Expression(`objDelRaw(${curr}, ${prop})`);
}
return curr;
},
] as types.OperatorDefinition<types.ExprObjectSet>,
];
3 changes: 2 additions & 1 deletion src/json-expression/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,13 @@ export type ExprMap = TernaryExpression<'map'>;
export type ExprReduce = QuinaryExpression<'reduce'>;

// Object expressions
export type ObjectExpression = ExprKeys | ExprValues | ExprEntries | ExprObjectSet;
export type ObjectExpression = ExprKeys | ExprValues | ExprEntries | ExprObjectSet | ExprObjectDel;

export type ExprKeys = UnaryExpression<'keys'>;
export type ExprValues = UnaryExpression<'values'>;
export type ExprEntries = UnaryExpression<'entries'>;
export type ExprObjectSet = VariadicExpression<'o.set'>;
export type ExprObjectDel = VariadicExpression<'o.del'>;

// Bitwise expressions
export type BitwiseExpression = ExprBitAnd | ExprBitOr | ExprBitXor | ExprBitNot;
Expand Down
5 changes: 5 additions & 0 deletions src/json-expression/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ export const objSetRaw = (obj: Record<string, unknown>, key: string, value: unkn
return obj;
};

export const objDelRaw = (obj: Record<string, unknown>, key: string): Record<string, unknown> => {
delete obj[key];
return obj;
};

// -------------------------------------------------------------------- Various

export const isLiteral = (value: unknown): boolean => {
Expand Down

0 comments on commit 20c7aff

Please sign in to comment.