Skip to content

Commit

Permalink
feat(formula): add regexextract/regexmatch/regexreplace function
Browse files Browse the repository at this point in the history
  • Loading branch information
wpxp123456 authored and wpxp123456 committed Sep 2, 2024
1 parent c7b5319 commit 2232a4a
Show file tree
Hide file tree
Showing 15 changed files with 795 additions and 16 deletions.
20 changes: 20 additions & 0 deletions packages/engine-formula/src/engine/utils/check-variant-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ export function checkVariantErrorIsArray(variant: BaseValueObject): BaseValueObj
return _variant;
}

export function checkVariantsErrorIsArray(...variants: BaseValueObject[]) {
for (let i = 0; i < variants.length; i++) {
const variant = checkVariantErrorIsArray(variants[i]);

if (variant.isError()) {
return {
isError: true,
errorObject: variant,
};
}

variants[i] = variant;
}

return {
isError: false,
variants,
};
}

export function checkVariantsErrorIsArrayOrBoolean(...variants: BaseValueObject[]) {
for (let i = 0; i < variants.length; i++) {
const variant = checkVariantErrorIsArray(variants[i]);
Expand Down
6 changes: 6 additions & 0 deletions packages/engine-formula/src/functions/text/function-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { Concatenate } from './concatenate';
import { Len } from './len';
import { Lenb } from './lenb';
import { Lower } from './lower';
import { Regexextract } from './regexextract';
import { Regexmatch } from './regexmatch';
import { Regexreplace } from './regexreplace';
import { Rept } from './rept';
import { Text } from './text';
import { Textafter } from './textafter';
Expand All @@ -32,6 +35,9 @@ export const functionText = [
[Len, FUNCTION_NAMES_TEXT.LEN],
[Lenb, FUNCTION_NAMES_TEXT.LENB],
[Lower, FUNCTION_NAMES_TEXT.LOWER],
[Regexextract, FUNCTION_NAMES_TEXT.REGEXEXTRACT],
[Regexmatch, FUNCTION_NAMES_TEXT.REGEXMATCH],
[Regexreplace, FUNCTION_NAMES_TEXT.REGEXREPLACE],
[Rept, FUNCTION_NAMES_TEXT.REPT],
[Text, FUNCTION_NAMES_TEXT.TEXT],
[Textafter, FUNCTION_NAMES_TEXT.TEXTAFTER],
Expand Down
3 changes: 3 additions & 0 deletions packages/engine-formula/src/functions/text/function-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export enum FUNCTION_NAMES_TEXT {
NUMBERVALUE = 'NUMBERVALUE',
PHONETIC = 'PHONETIC',
PROPER = 'PROPER',
REGEXEXTRACT = 'REGEXEXTRACT',
REGEXMATCH = 'REGEXMATCH',
REGEXREPLACE = 'REGEXREPLACE',
REPLACE = 'REPLACE',
REPLACEB = 'REPLACEB',
REPT = 'REPT',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, it } from 'vitest';

import { FUNCTION_NAMES_TEXT } from '../../function-names';
import { Regexextract } from '../index';
import { BooleanValueObject, NullValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';

describe('Test regexextract function', () => {
const testFunction = new Regexextract(FUNCTION_NAMES_TEXT.REGEXEXTRACT);

describe('Regexextract', () => {
it('Value is normal', () => {
const text = StringValueObject.create('abcdefg');
const regularExpression = StringValueObject.create('c.*f');
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual('cdef');
});

it('Value is boolean', () => {
const text = BooleanValueObject.create(true);
const regularExpression = StringValueObject.create('c.*f');
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(ErrorType.NA);

const text2 = StringValueObject.create('abcdefg');
const regularExpression2 = BooleanValueObject.create(true);
const result2 = testFunction.calculate(text2, regularExpression2);
expect(result2.getValue()).toStrictEqual(ErrorType.NA);
});

it('Value is blank cell', () => {
const text = StringValueObject.create('abcdefg');
const regularExpression = NullValueObject.create();
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual('');

const text2 = NullValueObject.create();
const regularExpression2 = StringValueObject.create('c.*f');
const result2 = testFunction.calculate(text2, regularExpression2);
expect(result2.getValue()).toStrictEqual(ErrorType.NA);
});

it('Value is error', () => {
const text = StringValueObject.create('abcdefg');
const regularExpression = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(ErrorType.NAME);
});

it('Value is array', () => {
const text = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, true, false, null, '***TRUETRUEFALSE'],
]),
rowCount: 1,
columnCount: 5,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const regularExpression = StringValueObject.create('c.*f');
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(ErrorType.VALUE);
});
});
});
75 changes: 75 additions & 0 deletions packages/engine-formula/src/functions/text/regexextract/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ErrorType } from '../../../basics/error-type';
import { checkVariantsErrorIsArray } from '../../../engine/utils/check-variant-error';
import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { StringValueObject } from '../../../engine/value-object/primitive-object';
import { BaseFunction } from '../../base-function';

export class Regexextract extends BaseFunction {
override minParams = 2;

override maxParams = 2;

override calculate(text: BaseValueObject, regularExpression: BaseValueObject): BaseValueObject {
const { isError, errorObject, variants } = checkVariantsErrorIsArray(text, regularExpression);

if (isError) {
return errorObject as ErrorValueObject;
}

const [textObject, regularExpressionObject] = variants as BaseValueObject[];

let textValue = textObject.getValue();

if (textObject.isNull()) {
textValue = '';
}

if (textObject.isBoolean()) {
textValue = textValue ? 'TRUE' : 'FALSE';
}

textValue = `${textValue}`;

let regularExpressionValue = regularExpressionObject.getValue();

if (regularExpressionObject.isNull()) {
return StringValueObject.create('');
}

if (regularExpressionObject.isBoolean()) {
regularExpressionValue = regularExpressionValue ? 'TRUE' : 'FALSE';
}

regularExpressionValue = `${regularExpressionValue}`;

try {
const regex = new RegExp(regularExpressionValue, 'g');

const result = textValue.match(regex);

if (result === null) {
return ErrorValueObject.create(ErrorType.NA);
}

return StringValueObject.create(result[0]);
} catch (error) {
throw new Error(`Invalid regular expression: ${regularExpressionValue}. \n${error}`);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, it } from 'vitest';

import { FUNCTION_NAMES_TEXT } from '../../function-names';
import { Regexmatch } from '../index';
import { BooleanValueObject, NullValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object';
import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object';
import { ErrorType } from '../../../../basics/error-type';
import { ErrorValueObject } from '../../../../engine/value-object/base-value-object';

describe('Test regexmatch function', () => {
const testFunction = new Regexmatch(FUNCTION_NAMES_TEXT.REGEXMATCH);

describe('Regexmatch', () => {
it('Value is normal', () => {
const text = StringValueObject.create('abcdefg');
const regularExpression = StringValueObject.create('c.*f');
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(true);
});

it('Value is boolean', () => {
const text = BooleanValueObject.create(true);
const regularExpression = StringValueObject.create('c.*f');
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(false);

const text2 = StringValueObject.create('abcdefg');
const regularExpression2 = BooleanValueObject.create(true);
const result2 = testFunction.calculate(text2, regularExpression2);
expect(result2.getValue()).toStrictEqual(false);
});

it('Value is blank cell', () => {
const text = StringValueObject.create('abcdefg');
const regularExpression = NullValueObject.create();
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(true);

const text2 = NullValueObject.create();
const regularExpression2 = StringValueObject.create('c.*f');
const result2 = testFunction.calculate(text2, regularExpression2);
expect(result2.getValue()).toStrictEqual(false);
});

it('Value is error', () => {
const text = StringValueObject.create('abcdefg');
const regularExpression = ErrorValueObject.create(ErrorType.NAME);
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(ErrorType.NAME);
});

it('Value is array', () => {
const text = ArrayValueObject.create({
calculateValueList: transformToValueObject([
[1, true, false, null, '***TRUETRUEFALSE'],
]),
rowCount: 1,
columnCount: 5,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
const regularExpression = StringValueObject.create('c.*f');
const result = testFunction.calculate(text, regularExpression);
expect(result.getValue()).toStrictEqual(ErrorType.VALUE);
});
});
});
74 changes: 74 additions & 0 deletions packages/engine-formula/src/functions/text/regexmatch/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { checkVariantsErrorIsArray } from '../../../engine/utils/check-variant-error';
import type { BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BooleanValueObject } from '../../../engine/value-object/primitive-object';
import { BaseFunction } from '../../base-function';

export class Regexmatch extends BaseFunction {
override minParams = 2;

override maxParams = 2;

override calculate(text: BaseValueObject, regularExpression: BaseValueObject): BaseValueObject {
const { isError, errorObject, variants } = checkVariantsErrorIsArray(text, regularExpression);

if (isError) {
return errorObject as ErrorValueObject;
}

const [textObject, regularExpressionObject] = variants as BaseValueObject[];

let textValue = textObject.getValue();

if (textObject.isNull()) {
textValue = '';
}

if (textObject.isBoolean()) {
textValue = textValue ? 'TRUE' : 'FALSE';
}

textValue = `${textValue}`;

let regularExpressionValue = regularExpressionObject.getValue();

if (regularExpressionObject.isNull()) {
return BooleanValueObject.create(true);
}

if (regularExpressionObject.isBoolean()) {
regularExpressionValue = regularExpressionValue ? 'TRUE' : 'FALSE';
}

regularExpressionValue = `${regularExpressionValue}`;

try {
const regex = new RegExp(regularExpressionValue, 'g');

const result = textValue.match(regex);

if (result === null) {
return BooleanValueObject.create(false);
}

return BooleanValueObject.create(true);
} catch (error) {
throw new Error(`Invalid regular expression: ${regularExpressionValue}. \n${error}`);
}
}
}
Loading

0 comments on commit 2232a4a

Please sign in to comment.