From 114f6dc102d402e2fb00da161b61063201317205 Mon Sep 17 00:00:00 2001 From: wpxp123456 <2677556700@qq.com> Date: Tue, 5 Nov 2024 17:32:07 +0800 Subject: [PATCH] feat(formula): supplement text formulas (#3842) Co-authored-by: wpxp123456 --- packages/engine-formula/src/basics/format.ts | 25 ++ .../src/engine/utils/char-kit.ts | 12 - .../__tests__/nested-functions.spec.ts | 2 +- .../text/find/__test__/index.spec.ts | 143 +++++++++++ .../src/functions/text/find/index.ts | 106 +++++++++ .../text/findb/__test__/index.spec.ts | 148 ++++++++++++ .../src/functions/text/findb/index.ts | 109 +++++++++ .../src/functions/text/function-map.ts | 24 +- .../text/left/__test__/index.spec.ts | 100 ++++++++ .../src/functions/text/left/index.ts | 97 ++++++++ .../text/leftb/__test__/index.spec.ts | 49 ++-- .../src/functions/text/leftb/index.ts | 7 +- .../functions/text/len/__test__/index.spec.ts | 62 ++++- .../src/functions/text/len/index.ts | 38 +-- .../text/lenb/__test__/index.spec.ts | 35 ++- .../src/functions/text/lenb/index.ts | 32 +-- .../text/lower/__test__/index.spec.ts | 63 +++-- .../src/functions/text/lower/index.ts | 31 ++- .../functions/text/mid/__test__/index.spec.ts | 225 ++++++------------ .../src/functions/text/mid/index.ts | 103 ++++---- .../text/midb/__test__/index.spec.ts | 128 ++++++++++ .../src/functions/text/midb/index.ts | 111 +++++++++ .../text/replace/__test__/index.spec.ts | 151 ++++++++++++ .../src/functions/text/replace/index.ts | 105 ++++++++ .../text/replaceb/__test__/index.spec.ts | 151 ++++++++++++ .../src/functions/text/replaceb/index.ts | 118 +++++++++ .../text/right/__test__/index.spec.ts | 9 +- .../src/functions/text/right/index.ts | 13 +- .../text/rightb/__test__/index.spec.ts | 11 +- .../src/functions/text/rightb/index.ts | 13 +- .../text/search/__test__/index.spec.ts | 143 +++++++++++ .../src/functions/text/search/index.ts | 106 +++++++++ .../text/searchb/__test__/index.spec.ts | 148 ++++++++++++ .../src/functions/text/searchb/index.ts | 109 +++++++++ .../text/textjoin/__test__/index.spec.ts | 170 +++++++++++++ .../src/functions/text/textjoin/index.ts | 164 +++++++++++++ .../text/trim/__test__/index.spec.ts | 84 +++++++ .../src/functions/text/trim/index.ts | 60 +++++ .../text/upper/__test__/index.spec.ts | 80 +++++++ .../src/functions/text/upper/index.ts | 54 +++++ .../src/locale/function-list/text/en-US.ts | 66 ++--- .../src/locale/function-list/text/ja-JP.ts | 70 +++--- .../src/locale/function-list/text/ru-RU.ts | 214 ++++++++++------- .../src/locale/function-list/text/vi-VN.ts | 159 +++++++++---- .../src/locale/function-list/text/zh-CN.ts | 66 ++--- .../src/locale/function-list/text/zh-TW.ts | 116 ++++----- .../src/services/function-list/text.ts | 221 +++++++++++------ 47 files changed, 3535 insertions(+), 716 deletions(-) create mode 100644 packages/engine-formula/src/functions/text/find/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/find/index.ts create mode 100644 packages/engine-formula/src/functions/text/findb/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/findb/index.ts create mode 100644 packages/engine-formula/src/functions/text/left/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/left/index.ts create mode 100644 packages/engine-formula/src/functions/text/midb/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/midb/index.ts create mode 100644 packages/engine-formula/src/functions/text/replace/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/replace/index.ts create mode 100644 packages/engine-formula/src/functions/text/replaceb/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/replaceb/index.ts create mode 100644 packages/engine-formula/src/functions/text/search/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/search/index.ts create mode 100644 packages/engine-formula/src/functions/text/searchb/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/searchb/index.ts create mode 100644 packages/engine-formula/src/functions/text/textjoin/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/textjoin/index.ts create mode 100644 packages/engine-formula/src/functions/text/trim/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/trim/index.ts create mode 100644 packages/engine-formula/src/functions/text/upper/__test__/index.spec.ts create mode 100644 packages/engine-formula/src/functions/text/upper/index.ts diff --git a/packages/engine-formula/src/basics/format.ts b/packages/engine-formula/src/basics/format.ts index aa1a1b9d643..8b0fec5ecc3 100644 --- a/packages/engine-formula/src/basics/format.ts +++ b/packages/engine-formula/src/basics/format.ts @@ -14,7 +14,9 @@ * limitations under the License. */ +import type { BaseValueObject } from '../engine/value-object/base-value-object'; import { numfmt } from '@univerjs/core'; +import { stripErrorMargin } from '../engine/utils/math-kit'; /** * covert number to preview string by pattern @@ -27,3 +29,26 @@ import { numfmt } from '@univerjs/core'; export const getFormatPreview = (pattern: string, value: number) => { return numfmt.format(pattern, value, { throws: false }); }; + +export const getTextValueOfNumberFormat = (text: BaseValueObject): string => { + let textValue = `${text.getValue()}`; + + if (text.isNull()) { + textValue = ''; + } + + if (text.isBoolean()) { + textValue = textValue.toLocaleUpperCase(); + } + + if (text.isNumber()) { + if (text.getPattern() !== '') { + textValue = getFormatPreview(text.getPattern(), +text.getValue()); + } else { + // Specify Number.EPSILON to not discard necessary digits in the case of non-precision errors, for example, the length of 1/3 is 17 + textValue = `${stripErrorMargin(+text.getValue())}`; + } + } + + return textValue; +}; diff --git a/packages/engine-formula/src/engine/utils/char-kit.ts b/packages/engine-formula/src/engine/utils/char-kit.ts index ff2b927d9b6..3f0ad86abe3 100644 --- a/packages/engine-formula/src/engine/utils/char-kit.ts +++ b/packages/engine-formula/src/engine/utils/char-kit.ts @@ -24,18 +24,6 @@ export function charLenByte(text: string): number { let byteCount = 0; for (let i = 0; i < text.length; i++) { - // const charCode = text.charCodeAt(i); - - // if ( - // (charCode >= 0x3040 && charCode <= 0x30FF) || // Japanese hiragana and katakana - // (charCode >= 0x4E00 && charCode <= 0x9FFF) || // Chinese (simplified and traditional) - // (charCode >= 0xAC00 && charCode <= 0xD7AF) // Korean language - // ) { - // byteCount += 2; - // } else { - // byteCount += 1; - // } - byteCount += getCharLenByteInText(text, i); } diff --git a/packages/engine-formula/src/functions/__tests__/nested-functions.spec.ts b/packages/engine-formula/src/functions/__tests__/nested-functions.spec.ts index b3ffb8bb07c..1e76184c0bb 100644 --- a/packages/engine-formula/src/functions/__tests__/nested-functions.spec.ts +++ b/packages/engine-formula/src/functions/__tests__/nested-functions.spec.ts @@ -391,7 +391,7 @@ describe('Test nested functions', () => { it('Len gets length from formula result', () => { let result = calculate('=LEN(1/3)'); - expect(result).toStrictEqual(17); + expect(result).toStrictEqual(14); result = calculate('=LEN(0.1+0.2)'); expect(result).toStrictEqual(3); diff --git a/packages/engine-formula/src/functions/text/find/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/find/__test__/index.spec.ts new file mode 100644 index 00000000000..57011d070cb --- /dev/null +++ b/packages/engine-formula/src/functions/text/find/__test__/index.spec.ts @@ -0,0 +1,143 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Find } from '../index'; + +describe('Test find function', () => { + const testFunction = new Find(FUNCTION_NAMES_TEXT.FIND); + + describe('Find', () => { + it('Value is normal', () => { + const findText = StringValueObject.create('Univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(7); + }); + + it('FindText value test', () => { + const findText = NullValueObject.create(); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(1); + + const findText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText2, withinText, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const findText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText3, withinText, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const findText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText4, withinText, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('WithinText value test', () => { + const findText = StringValueObject.create('Univer'); + const withinText = NullValueObject.create(); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const withinText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText, withinText2, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const withinText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText3, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const withinText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText4, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('StartNum value test', () => { + const findText = StringValueObject.create('Univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NullValueObject.create(); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(findText, withinText, startNum2); + expect(getObjectValue(result2)).toStrictEqual(7); + + const startNum3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText, startNum3); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const startNum4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText, startNum4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const startNum5 = NumberValueObject.create(13); + const result5 = testFunction.calculate(findText, withinText, startNum5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('Value is array', () => { + const findText = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, 'm', '2.34', 'M', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const withinText = StringValueObject.create('Miriam McGovern'); + const startNum = NumberValueObject.create(2); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [ErrorType.VALUE, 7, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, 2], + [ErrorType.VALUE, 6, ErrorType.VALUE, 8, ErrorType.VALUE, ErrorType.NAME], + ]); + }); + + it('More test', () => { + const findText = StringValueObject.create('o'); + const withinText = StringValueObject.create('Hello中文o😊Wo😊rld'); + const startNum = ArrayValueObject.create('{1,6,9,13}'); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [5, 8, 12, ErrorType.VALUE], + ]); + + const findText2 = StringValueObject.create('2'); + const withinText2 = StringValueObject.create('2012-2-2'); + const startNum2 = ArrayValueObject.create('{1,2,5,7}'); + const result2 = testFunction.calculate(findText2, withinText2, startNum2); + expect(getObjectValue(result2)).toStrictEqual([ + [1, 4, 6, 8], + ]); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/find/index.ts b/packages/engine-formula/src/functions/text/find/index.ts new file mode 100644 index 00000000000..3c1134dae2a --- /dev/null +++ b/packages/engine-formula/src/functions/text/find/index.ts @@ -0,0 +1,106 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; +import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import { NumberValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Find extends BaseFunction { + override minParams = 2; + + override maxParams = 3; + + override calculate(findText: BaseValueObject, withinText: BaseValueObject, startNum?: BaseValueObject): BaseValueObject { + const _startNum = startNum ?? NumberValueObject.create(1); + + const maxRowLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getRowCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getRowCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getColumnCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getColumnCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getColumnCount() : 1 + ); + + const findTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, findText, ErrorValueObject.create(ErrorType.NA)); + const withinTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, withinText, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, _startNum, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = findTextArray.mapValue((findTextObject, rowIndex, columnIndex) => { + const withinTextObject = withinTextArray.get(rowIndex, columnIndex) as BaseValueObject; + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (findTextObject.isError()) { + return findTextObject; + } + + if (withinTextObject.isError()) { + return withinTextObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + return this._handleSingleObject(findTextObject, withinTextObject, startNumObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(findText: BaseValueObject, withinText: BaseValueObject, startNum: BaseValueObject): BaseValueObject { + const findTextValue = getTextValueOfNumberFormat(findText); + const withinTextValue = getTextValueOfNumberFormat(withinText); + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + + if (withinText.isNull() || startNumValue <= 0 || startNumValue > withinTextValue.length) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + if (findText.isNull() || findTextValue.length === 0) { + return NumberValueObject.create(startNumValue); + } + + const result = withinTextValue.indexOf(findTextValue, startNumValue - 1); + + if (result === -1) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + return NumberValueObject.create(result + 1); + } +} diff --git a/packages/engine-formula/src/functions/text/findb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/findb/__test__/index.spec.ts new file mode 100644 index 00000000000..5e87e01339f --- /dev/null +++ b/packages/engine-formula/src/functions/text/findb/__test__/index.spec.ts @@ -0,0 +1,148 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Findb } from '../index'; + +describe('Test findb function', () => { + const testFunction = new Findb(FUNCTION_NAMES_TEXT.FINDB); + + describe('Findb', () => { + it('Value is normal', () => { + const findText = StringValueObject.create('Univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(7); + }); + + it('FindText value test', () => { + const findText = NullValueObject.create(); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(1); + + const findText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText2, withinText, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const findText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText3, withinText, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const findText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText4, withinText, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('WithinText value test', () => { + const findText = StringValueObject.create('Univer'); + const withinText = NullValueObject.create(); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const withinText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText, withinText2, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const withinText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText3, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const withinText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText4, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('StartNum value test', () => { + const findText = StringValueObject.create('Univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NullValueObject.create(); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(findText, withinText, startNum2); + expect(getObjectValue(result2)).toStrictEqual(7); + + const startNum3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText, startNum3); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const startNum4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText, startNum4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const startNum5 = NumberValueObject.create(13); + const result5 = testFunction.calculate(findText, withinText, startNum5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('Value is array', () => { + const findText = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, 'm', '2.34', 'M', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const withinText = StringValueObject.create('Miriam McGovern'); + const startNum = NumberValueObject.create(2); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [ErrorType.VALUE, 7, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, 2], + [ErrorType.VALUE, 6, ErrorType.VALUE, 8, ErrorType.VALUE, ErrorType.NAME], + ]); + }); + + it('More test', () => { + const findText = StringValueObject.create('o'); + const withinText = StringValueObject.create('Hello中文o😊Wo😊rld'); + const startNum = ArrayValueObject.create('{1,6,9,13}'); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [5, 10, 16, ErrorType.VALUE], + ]); + + const findText2 = StringValueObject.create('欢迎'); + const withinText2 = StringValueObject.create('你好,欢迎'); + const result2 = testFunction.calculate(findText2, withinText2); + expect(getObjectValue(result2)).toStrictEqual(7); + + const findText3 = StringValueObject.create('2'); + const withinText3 = StringValueObject.create('2012-2-2'); + const startNum3 = ArrayValueObject.create('{1,2,5,7}'); + const result3 = testFunction.calculate(findText3, withinText3, startNum3); + expect(getObjectValue(result3)).toStrictEqual([ + [1, 4, 6, 8], + ]); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/findb/index.ts b/packages/engine-formula/src/functions/text/findb/index.ts new file mode 100644 index 00000000000..a86ea25cfdf --- /dev/null +++ b/packages/engine-formula/src/functions/text/findb/index.ts @@ -0,0 +1,109 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { charLenByte } from '../../../engine/utils/char-kit'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; +import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import { NumberValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Findb extends BaseFunction { + override minParams = 2; + + override maxParams = 3; + + override calculate(findText: BaseValueObject, withinText: BaseValueObject, startNum?: BaseValueObject): BaseValueObject { + const _startNum = startNum ?? NumberValueObject.create(1); + + const maxRowLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getRowCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getRowCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getColumnCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getColumnCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getColumnCount() : 1 + ); + + const findTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, findText, ErrorValueObject.create(ErrorType.NA)); + const withinTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, withinText, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, _startNum, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = findTextArray.mapValue((findTextObject, rowIndex, columnIndex) => { + const withinTextObject = withinTextArray.get(rowIndex, columnIndex) as BaseValueObject; + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (findTextObject.isError()) { + return findTextObject; + } + + if (withinTextObject.isError()) { + return withinTextObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + return this._handleSingleObject(findTextObject, withinTextObject, startNumObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(findText: BaseValueObject, withinText: BaseValueObject, startNum: BaseValueObject): BaseValueObject { + const findTextValue = getTextValueOfNumberFormat(findText); + const withinTextValue = getTextValueOfNumberFormat(withinText); + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + + if (withinText.isNull() || startNumValue <= 0 || startNumValue > withinTextValue.length) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + if (findText.isNull() || findTextValue.length === 0) { + return NumberValueObject.create(startNumValue); + } + + const index = withinTextValue.indexOf(findTextValue, startNumValue - 1); + + if (index === -1) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + const result = charLenByte(withinTextValue.substring(0, index)) + 1; + + return NumberValueObject.create(result); + } +} diff --git a/packages/engine-formula/src/functions/text/function-map.ts b/packages/engine-formula/src/functions/text/function-map.ts index bbde97ccfa0..44e411b570f 100644 --- a/packages/engine-formula/src/functions/text/function-map.ts +++ b/packages/engine-formula/src/functions/text/function-map.ts @@ -25,29 +25,40 @@ import { Concatenate } from './concatenate'; import { Dbcs } from './dbcs'; import { Dollar } from './dollar'; import { Exact } from './exact'; +import { Find } from './find'; +import { Findb } from './findb'; import { Fixed } from './fixed'; import { FUNCTION_NAMES_TEXT } from './function-names'; +import { Left } from './left'; import { Leftb } from './leftb'; import { Len } from './len'; import { Lenb } from './lenb'; import { Lower } from './lower'; import { Mid } from './mid'; +import { Midb } from './midb'; import { Numbervalue } from './numbervalue'; import { Proper } from './proper'; import { Regexextract } from './regexextract'; import { Regexmatch } from './regexmatch'; import { Regexreplace } from './regexreplace'; +import { Replace } from './replace'; +import { Replaceb } from './replaceb'; import { Rept } from './rept'; import { Right } from './right'; import { Rightb } from './rightb'; +import { Search } from './search'; +import { Searchb } from './searchb'; import { Substitute } from './substitute'; import { T } from './t'; import { Text } from './text'; import { Textafter } from './textafter'; import { Textbefore } from './textbefore'; +import { Textjoin } from './textjoin'; import { Textsplit } from './textsplit'; +import { Trim } from './trim'; import { Unichar } from './unichar'; import { Unicode } from './unicode'; +import { Upper } from './upper'; import { Value } from './value'; import { Valuetotext } from './valuetotext'; @@ -63,28 +74,39 @@ export const functionText = [ [Dbcs, FUNCTION_NAMES_TEXT.DBCS], [Dollar, FUNCTION_NAMES_TEXT.DOLLAR], [Exact, FUNCTION_NAMES_TEXT.EXACT], + [Find, FUNCTION_NAMES_TEXT.FIND], + [Findb, FUNCTION_NAMES_TEXT.FINDB], [Fixed, FUNCTION_NAMES_TEXT.FIXED], + [Left, FUNCTION_NAMES_TEXT.LEFT], [Leftb, FUNCTION_NAMES_TEXT.LEFTB], [Len, FUNCTION_NAMES_TEXT.LEN], [Lenb, FUNCTION_NAMES_TEXT.LENB], [Lower, FUNCTION_NAMES_TEXT.LOWER], [Mid, FUNCTION_NAMES_TEXT.MID], + [Midb, FUNCTION_NAMES_TEXT.MIDB], + [Numbervalue, FUNCTION_NAMES_TEXT.NUMBERVALUE], [Regexextract, FUNCTION_NAMES_TEXT.REGEXEXTRACT], [Regexmatch, FUNCTION_NAMES_TEXT.REGEXMATCH], [Regexreplace, FUNCTION_NAMES_TEXT.REGEXREPLACE], - [Numbervalue, FUNCTION_NAMES_TEXT.NUMBERVALUE], [Proper, FUNCTION_NAMES_TEXT.PROPER], + [Replace, FUNCTION_NAMES_TEXT.REPLACE], + [Replaceb, FUNCTION_NAMES_TEXT.REPLACEB], [Rept, FUNCTION_NAMES_TEXT.REPT], [Right, FUNCTION_NAMES_TEXT.RIGHT], [Rightb, FUNCTION_NAMES_TEXT.RIGHTB], + [Search, FUNCTION_NAMES_TEXT.SEARCH], + [Searchb, FUNCTION_NAMES_TEXT.SEARCHB], [Substitute, FUNCTION_NAMES_TEXT.SUBSTITUTE], [T, FUNCTION_NAMES_TEXT.T], [Text, FUNCTION_NAMES_TEXT.TEXT], [Textafter, FUNCTION_NAMES_TEXT.TEXTAFTER], [Textbefore, FUNCTION_NAMES_TEXT.TEXTBEFORE], + [Textjoin, FUNCTION_NAMES_TEXT.TEXTJOIN], [Textsplit, FUNCTION_NAMES_TEXT.TEXTSPLIT], + [Trim, FUNCTION_NAMES_TEXT.TRIM], [Unichar, FUNCTION_NAMES_TEXT.UNICHAR], [Unicode, FUNCTION_NAMES_TEXT.UNICODE], + [Upper, FUNCTION_NAMES_TEXT.UPPER], [Value, FUNCTION_NAMES_TEXT.VALUE], [Valuetotext, FUNCTION_NAMES_TEXT.VALUETOTEXT], ]; diff --git a/packages/engine-formula/src/functions/text/left/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/left/__test__/index.spec.ts new file mode 100644 index 00000000000..0c1eeeb17b8 --- /dev/null +++ b/packages/engine-formula/src/functions/text/left/__test__/index.spec.ts @@ -0,0 +1,100 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Left } from '../index'; + +describe('Test left function', () => { + const testFunction = new Left(FUNCTION_NAMES_TEXT.LEFT); + + describe('Left', () => { + it('Value is normal', () => { + const text = StringValueObject.create('Univer'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual('U'); + }); + + it('NumChars value test', () => { + const text = StringValueObject.create('Univer'); + const numChars = NumberValueObject.create(-2); + const result = testFunction.calculate(text, numChars); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const numChars2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(text, numChars2); + expect(getObjectValue(result2)).toStrictEqual(''); + + const numChars3 = NumberValueObject.create(6); + const result3 = testFunction.calculate(text, numChars3); + expect(getObjectValue(result3)).toStrictEqual('Univer'); + + const numChars4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(text, numChars4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const numChars5 = StringValueObject.create('test'); + const result5 = testFunction.calculate(text, numChars5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('Value is array', () => { + const text = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, '2012-2-2', '2.34', '2-way street', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const numChars = NumberValueObject.create(2); + const result = testFunction.calculate(text, numChars); + expect(getObjectValue(result)).toStrictEqual([ + ['1', ' ', '中文', 'TR', 'FA', ''], + ['0', '20', '2.', '2-', '-3', ErrorType.NAME], + ]); + }); + + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const numChars = NumberValueObject.create(4); + const result = testFunction.calculate(text, numChars); + expect(getObjectValue(result)).toStrictEqual(',。、;'); + + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const numChars2 = ArrayValueObject.create('{3,5,7,10,15}'); + const result2 = testFunction.calculate(text2, numChars2); + expect(getObjectValue(result2)).toStrictEqual([ + ['Hel', 'Hello', 'Hello中文', 'Hello中文o😊', 'Hello中文o😊Wo😊r'], + ]); + + const text3 = NumberValueObject.create(0.01, '0%'); + const numChars3 = NumberValueObject.create(2); + const result3 = testFunction.calculate(text3, numChars3); + expect(getObjectValue(result3)).toStrictEqual('1%'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/left/index.ts b/packages/engine-formula/src/functions/text/left/index.ts new file mode 100644 index 00000000000..7b980ed1254 --- /dev/null +++ b/packages/engine-formula/src/functions/text/left/index.ts @@ -0,0 +1,97 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; +import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import { NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Left extends BaseFunction { + override minParams = 1; + + override maxParams = 2; + + override calculate(text: BaseValueObject, numChars?: BaseValueObject): BaseValueObject { + const _numChars = numChars ?? NumberValueObject.create(1); + + const maxRowLength = Math.max( + text.isArray() ? (text as ArrayValueObject).getRowCount() : 1, + _numChars.isArray() ? (_numChars as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + text.isArray() ? (text as ArrayValueObject).getColumnCount() : 1, + _numChars.isArray() ? (_numChars as ArrayValueObject).getColumnCount() : 1 + ); + + const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, ErrorValueObject.create(ErrorType.NA)); + const numCharsArray = expandArrayValueObject(maxRowLength, maxColumnLength, _numChars, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = textArray.mapValue((textObject, rowIndex, columnIndex) => { + const numCharsObject = numCharsArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (textObject.isError()) { + return textObject; + } + + if (numCharsObject.isError()) { + return numCharsObject; + } + + return this._handleSingleObject(textObject, numCharsObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(text: BaseValueObject, numChars: BaseValueObject): BaseValueObject { + const textValue = getTextValueOfNumberFormat(text); + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(numChars); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [numCharsObject] = variants as BaseValueObject[]; + + const numCharsValue = Math.floor(+numCharsObject.getValue()); + + if (numCharsValue < 0) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + if (text.isNull() || numCharsValue === 0) { + return StringValueObject.create(''); + } + + if (numCharsValue >= textValue.length) { + return StringValueObject.create(textValue); + } + + const result = textValue.substring(0, numCharsValue); + + return StringValueObject.create(result); + } +} diff --git a/packages/engine-formula/src/functions/text/leftb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/leftb/__test__/index.spec.ts index 191a9f2c3a3..23ce0285859 100644 --- a/packages/engine-formula/src/functions/text/leftb/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/leftb/__test__/index.spec.ts @@ -24,42 +24,42 @@ import { FUNCTION_NAMES_TEXT } from '../../function-names'; import { Leftb } from '../index'; describe('Test LEFTB function', () => { - const leftbFunction = new Leftb(FUNCTION_NAMES_TEXT.LEFTB); + const testFunction = new Leftb(FUNCTION_NAMES_TEXT.LEFTB); describe('Leftb', () => { describe('Single Value Tests', () => { it('Should return leftmost bytes of a single text', () => { const text = StringValueObject.create('Hello World'); const numBytes = NumberValueObject.create(5); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual('Hello'); }); it('Should return full text if byte length exceeds text length', () => { const text = StringValueObject.create('Hello'); const numBytes = NumberValueObject.create(10); // Exceeding text length - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual('Hello'); }); it('Should handle zero byte length correctly', () => { const text = StringValueObject.create('Hello'); const numBytes = NumberValueObject.create(0); // Zero byte length - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual(''); }); it('Should return ErrorType.VALUE for negative byte length', () => { const text = StringValueObject.create('Hello'); const numBytes = NumberValueObject.create(-1); // Negative byte length - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); }); it('Should handle emojis and byte length correctly', () => { const text = StringValueObject.create('😊Hello'); const numBytes = NumberValueObject.create(3); // Bytes needed to capture '😊' - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual('😊'); }); }); @@ -80,7 +80,7 @@ describe('Test LEFTB function', () => { column: 0, }); const numBytes = NumberValueObject.create(3); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual([ ['Hel'], // First character of 'Hello' ['Wor'], // First two characters of 'World' @@ -118,7 +118,7 @@ describe('Test LEFTB function', () => { row: 0, column: 0, }); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual([ ['H'], // First character of 'Hello' ['Wo'], // First two characters of 'World' @@ -142,7 +142,7 @@ describe('Test LEFTB function', () => { row: 0, column: 0, }); - const result = leftbFunction.calculate(text); + const result = testFunction.calculate(text); expect(getObjectValue(result)).toStrictEqual([ ['H'], // Default to first character of 'Hello' ['W'], // Default to first character of 'World' @@ -154,14 +154,14 @@ describe('Test LEFTB function', () => { it('Handles extracting from text with emojis', () => { const text = StringValueObject.create('Hello 😊 World'); const numBytes = NumberValueObject.create(9); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual('Hello 😊'); }); it('Handles extracting with numChars as zero', () => { const text = StringValueObject.create('Hello'); const numBytes = NumberValueObject.create(0); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual(''); }); @@ -179,7 +179,7 @@ describe('Test LEFTB function', () => { row: 0, column: 0, }); - const result = leftbFunction.calculate(text); + const result = testFunction.calculate(text); expect(getObjectValue(result)).toStrictEqual([ ['销'], ['か'], @@ -202,7 +202,7 @@ describe('Test LEFTB function', () => { column: 0, }); const numBytes = NumberValueObject.create(2); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual([ ['销'], ['か'], @@ -226,7 +226,7 @@ describe('Test LEFTB function', () => { column: 0, }); const numBytes = NumberValueObject.create(3); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual([ ['销售'], ['uか'], @@ -269,7 +269,7 @@ describe('Test LEFTB function', () => { row: 0, column: 0, }); - const result = leftbFunction.calculate(text, numBytes); + const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual([ ['1', ' ', '1', 'T', 'F', '', '0', '1', '2', 't', '-', ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], @@ -285,5 +285,24 @@ describe('Test LEFTB function', () => { [ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME], ]); }); + + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const numChars = NumberValueObject.create(4); + const result = testFunction.calculate(text, numChars); + expect(getObjectValue(result)).toStrictEqual(',。、'); + + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const numChars2 = ArrayValueObject.create('{3,5,7,10,15}'); + const result2 = testFunction.calculate(text2, numChars2); + expect(getObjectValue(result2)).toStrictEqual([ + ['Hel', 'Hello', 'Hello中', 'Hello中文o', 'Hello中文o😊W'], + ]); + + const text3 = NumberValueObject.create(0.01, '0%'); + const numChars3 = NumberValueObject.create(2); + const result3 = testFunction.calculate(text3, numChars3); + expect(getObjectValue(result3)).toStrictEqual('1%'); + }); }); }); diff --git a/packages/engine-formula/src/functions/text/leftb/index.ts b/packages/engine-formula/src/functions/text/leftb/index.ts index 76eb7950a03..d7214252f1e 100644 --- a/packages/engine-formula/src/functions/text/leftb/index.ts +++ b/packages/engine-formula/src/functions/text/leftb/index.ts @@ -16,6 +16,7 @@ import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; import { expandArrayValueObject } from '../../../engine/utils/array-object'; import { getCharLenByteInText } from '../../../engine/utils/char-kit'; import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; @@ -66,11 +67,7 @@ export class Leftb extends BaseFunction { } private _handleSingleObject(text: BaseValueObject, numBytes: BaseValueObject): BaseValueObject { - let textValue = `${text.getValue()}`; - - if (text.isBoolean()) { - textValue = textValue.toLocaleUpperCase(); - } + const textValue = getTextValueOfNumberFormat(text); const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(numBytes); diff --git a/packages/engine-formula/src/functions/text/len/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/len/__test__/index.spec.ts index 49eed6a1587..da8b91b5d4e 100644 --- a/packages/engine-formula/src/functions/text/len/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/len/__test__/index.spec.ts @@ -16,27 +16,28 @@ import { describe, expect, it } from 'vitest'; +import { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; import { FUNCTION_NAMES_TEXT } from '../../function-names'; import { Len } from '../index'; -import { StringValueObject } from '../../../../engine/value-object/primitive-object'; -import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object'; -import { ErrorType } from '../../../../basics/error-type'; describe('Test len function', () => { const testFunction = new Len(FUNCTION_NAMES_TEXT.LEN); describe('Len', () => { - it('Text is single cell', () => { + it('Value is normal', () => { const text = StringValueObject.create('Univer'); const result = testFunction.calculate(text); - expect(result.getValue()).toStrictEqual(6); + expect(getObjectValue(result)).toStrictEqual(6); }); - it('Text1 is array', () => { + it('Value is array', () => { const text = new ArrayValueObject({ calculateValueList: transformToValueObject([ [1, ' ', 1.23, true, false, null, 'Univer表格シート繁體한국인'], - [0, '100', '2.34', 'test', -3, ErrorType.VALUE, null], + [0, '100', '2.34', 'test', -3, ErrorType.NAME, null], ]), rowCount: 2, columnCount: 7, @@ -46,12 +47,26 @@ describe('Test len function', () => { column: 0, }); const result = testFunction.calculate(text); - expect(transformToValue(result.getArrayValue())).toStrictEqual([ + expect(getObjectValue(result)).toStrictEqual([ [1, 1, 4, 4, 5, 0, 16], - [1, 3, 4, 4, 2, '#VALUE!', 0], + [1, 3, 4, 4, 2, ErrorType.NAME, 0], ]); + + const text2 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + ['2012-2-2'], + ]), + rowCount: 1, + columnCount: 1, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual(8); }); - it('Text1 is array with emoji', () => { + it('Value is array with emoji', () => { const text = new ArrayValueObject({ calculateValueList: transformToValueObject([ ['u'], @@ -76,7 +91,32 @@ describe('Test len function', () => { column: 0, }); const result = testFunction.calculate(text); - expect(transformToValue(result.getArrayValue())).toStrictEqual([[1], [1], [1], [1], [1], [1], [1], [2], [2], [8], [2], [2], [1]]); + expect(getObjectValue(result)).toStrictEqual([ + [1], + [1], + [1], + [1], + [1], + [1], + [1], + [2], + [2], + [8], + [2], + [2], + [1], + ]); + }); + + it('More test', () => { + const text = NumberValueObject.create(0.01, '0%'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual(2); + + // eslint-disable-next-line + const text2 = NumberValueObject.create(1.22222222222222222); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual(13); }); }); }); diff --git a/packages/engine-formula/src/functions/text/len/index.ts b/packages/engine-formula/src/functions/text/len/index.ts index f69655cff9b..62674df7131 100644 --- a/packages/engine-formula/src/functions/text/len/index.ts +++ b/packages/engine-formula/src/functions/text/len/index.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { ErrorType } from '../../../basics/error-type'; -import { stripErrorMargin } from '../../../engine/utils/math-kit'; -import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import type { BaseValueObject } from '../../../engine/value-object/base-value-object'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; import { NumberValueObject } from '../../../engine/value-object/primitive-object'; import { BaseFunction } from '../../base-function'; @@ -26,14 +26,14 @@ export class Len extends BaseFunction { override maxParams = 1; override calculate(text: BaseValueObject) { - if (text.isError()) { - return text; - } - if (text.isArray()) { - return text.mapValue((textValue: BaseValueObject) => { - return this._handleSingleText(textValue); - }); + const resultArray = text.mapValue((textValue: BaseValueObject) => this._handleSingleText(textValue)); + + if ((resultArray as ArrayValueObject).getRowCount() === 1 && (resultArray as ArrayValueObject).getColumnCount() === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; } return this._handleSingleText(text); @@ -44,22 +44,8 @@ export class Len extends BaseFunction { return text; } - if (text.isNull()) { - return NumberValueObject.create(0); - } - - if (text.isNumber()) { - const numberValue = text.getValue() as number; - // Specify Number.EPSILON to not discard necessary digits in the case of non-precision errors, for example, the length of 1/3 is 17 - const numberValueString = stripErrorMargin(numberValue, 12, Number.EPSILON).toString(); - return NumberValueObject.create(numberValueString.length); - } - - if (text.isString() || text.isBoolean() || text.isNumber()) { - const textValue = text.getValue().toString(); - return NumberValueObject.create(textValue.length); - } + const textValue = getTextValueOfNumberFormat(text); - return ErrorValueObject.create(ErrorType.VALUE); + return NumberValueObject.create(textValue.length); } } diff --git a/packages/engine-formula/src/functions/text/lenb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/lenb/__test__/index.spec.ts index 36ec95a831e..942c91c2d79 100644 --- a/packages/engine-formula/src/functions/text/lenb/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/lenb/__test__/index.spec.ts @@ -18,7 +18,7 @@ import { describe, expect, it } from 'vitest'; import { ErrorType } from '../../../../basics/error-type'; import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; -import { StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; import { getObjectValue } from '../../../__tests__/create-function-test-bed'; import { FUNCTION_NAMES_TEXT } from '../../function-names'; import { Lenb } from '../index'; @@ -27,17 +27,17 @@ describe('Test lenb function', () => { const testFunction = new Lenb(FUNCTION_NAMES_TEXT.LENB); describe('Lenb', () => { - it('Text is single cell', () => { + it('Value is normal', () => { const text = StringValueObject.create('Univer'); const result = testFunction.calculate(text); expect(getObjectValue(result)).toStrictEqual(6); }); - it('Text1 is array', () => { - const text = new ArrayValueObject({ + it('Text value is array', () => { + const text = ArrayValueObject.create({ calculateValueList: transformToValueObject([ [1, ' ', 1.23, true, false, null, 'Univer表格シート繁體한국인'], - [0, '100', '2.34', 'test', -3, ErrorType.VALUE, null], + [0, '100', '2.34', 'test', -3, ErrorType.NAME, null], ]), rowCount: 2, columnCount: 7, @@ -49,8 +49,22 @@ describe('Test lenb function', () => { const result = testFunction.calculate(text); expect(getObjectValue(result)).toStrictEqual([ [1, 1, 4, 4, 5, 0, 26], - [1, 3, 4, 4, 2, '#VALUE!', 0], + [1, 3, 4, 4, 2, ErrorType.NAME, 0], ]); + + const text2 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + ['2012-2-2'], + ]), + rowCount: 1, + columnCount: 1, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual(8); }); it('Text1 is array with emoji', () => { @@ -104,6 +118,15 @@ describe('Test lenb function', () => { const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); const result2 = testFunction.calculate(text2); expect(getObjectValue(result2)).toStrictEqual(23); + + const text3 = NumberValueObject.create(0.01, '0%'); + const result3 = testFunction.calculate(text3); + expect(getObjectValue(result3)).toStrictEqual(2); + + // eslint-disable-next-line + const text4 = NumberValueObject.create(1.22222222222222222); + const result4 = testFunction.calculate(text4); + expect(getObjectValue(result4)).toStrictEqual(13); }); }); }); diff --git a/packages/engine-formula/src/functions/text/lenb/index.ts b/packages/engine-formula/src/functions/text/lenb/index.ts index 0bcba656ba4..6da0fbee87b 100644 --- a/packages/engine-formula/src/functions/text/lenb/index.ts +++ b/packages/engine-formula/src/functions/text/lenb/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ -import { ErrorType } from '../../../basics/error-type'; +import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import type { BaseValueObject } from '../../../engine/value-object/base-value-object'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; import { charLenByte } from '../../../engine/utils/char-kit'; -import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; import { NumberValueObject } from '../../../engine/value-object/primitive-object'; import { BaseFunction } from '../../base-function'; @@ -26,14 +27,14 @@ export class Lenb extends BaseFunction { override maxParams = 1; override calculate(text: BaseValueObject) { - if (text.isError()) { - return text; - } - if (text.isArray()) { - return text.mapValue((textValue: BaseValueObject) => { - return this._handleSingleText(textValue); - }); + const resultArray = text.mapValue((textValue: BaseValueObject) => this._handleSingleText(textValue)); + + if ((resultArray as ArrayValueObject).getRowCount() === 1 && (resultArray as ArrayValueObject).getColumnCount() === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; } return this._handleSingleText(text); @@ -44,17 +45,10 @@ export class Lenb extends BaseFunction { return text; } - if (text.isNull()) { - return NumberValueObject.create(0); - } - - if (text.isString() || text.isBoolean() || text.isNumber()) { - const textValue = `${text.getValue()}`; - const textByteLen = charLenByte(textValue); + const textValue = getTextValueOfNumberFormat(text); - return NumberValueObject.create(textByteLen); - } + const textByteLen = charLenByte(textValue); - return ErrorValueObject.create(ErrorType.VALUE); + return NumberValueObject.create(textByteLen); } } diff --git a/packages/engine-formula/src/functions/text/lower/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/lower/__test__/index.spec.ts index 643e69d2f62..ccde9b7b07a 100644 --- a/packages/engine-formula/src/functions/text/lower/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/lower/__test__/index.spec.ts @@ -16,52 +16,65 @@ import { describe, expect, it } from 'vitest'; +import { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; import { FUNCTION_NAMES_TEXT } from '../../function-names'; import { Lower } from '../index'; -import { BooleanValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; -import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object'; -import { ErrorType } from '../../../../basics/error-type'; describe('Test lower function', () => { const testFunction = new Lower(FUNCTION_NAMES_TEXT.LOWER); describe('Lower', () => { it('Value is normal', () => { - const value = StringValueObject.create('Apt. 2B'); - const result = testFunction.calculate(value); - expect(result.getValue()).toBe('apt. 2b'); - }); - - it('Value is number', () => { - const value = NumberValueObject.create(1); - const result = testFunction.calculate(value); - expect(result.getValue()).toBe('1'); - }); - - it('Value is boolean', () => { - const value = BooleanValueObject.create(true); - const result = testFunction.calculate(value); - expect(result.getValue()).toBe('true'); + const text = StringValueObject.create('Univer'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual('univer'); }); it('Value is array', () => { - const text = new ArrayValueObject({ + const text = ArrayValueObject.create({ calculateValueList: transformToValueObject([ - [1, ' ', 1.23, true, false, null, 'Univer表格シート繁體한국인'], - [0, '100', '2.34', 'TEST', -3, ErrorType.VALUE, null], + [1, ' ', '中文测试', true, false, null], + [0, '100', '2.34', '2-Way Street', -3, ErrorType.NAME], ]), rowCount: 2, - columnCount: 7, + columnCount: 6, unitId: '', sheetId: '', row: 0, column: 0, }); const result = testFunction.calculate(text); - expect(transformToValue(result.getArrayValue())).toStrictEqual([ - ['1', ' ', '1.23', 'true', 'false', '', 'univer表格シート繁體한국인'], - ['0', '100', '2.34', 'test', '-3', '#VALUE!', ''], + expect(getObjectValue(result)).toStrictEqual([ + ['1', ' ', '中文测试', 'true', 'false', ''], + ['0', '100', '2.34', '2-way street', '-3', ErrorType.NAME], ]); + + const text2 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [' Hello Univer '], + ]), + rowCount: 1, + columnCount: 1, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual(' hello univer '); + }); + + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual(',。、;:{}'); + + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual('hello中文o😊wo😊rld'); }); }); }); diff --git a/packages/engine-formula/src/functions/text/lower/index.ts b/packages/engine-formula/src/functions/text/lower/index.ts index 6209c2fe97c..478dad7d281 100644 --- a/packages/engine-formula/src/functions/text/lower/index.ts +++ b/packages/engine-formula/src/functions/text/lower/index.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { ErrorType } from '../../../basics/error-type'; -import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import type { BaseValueObject } from '../../../engine/value-object/base-value-object'; import { StringValueObject } from '../../../engine/value-object/primitive-object'; import { BaseFunction } from '../../base-function'; @@ -24,21 +24,21 @@ export class Lower extends BaseFunction { override maxParams = 1; - override calculate(text: BaseValueObject) { - if (text.isError()) { - return text; - } - + override calculate(text: BaseValueObject): BaseValueObject { if (text.isArray()) { - return text.mapValue((textValue: BaseValueObject) => { - return this._handleSingleText(textValue); - }); + const resultArray = text.mapValue((textObject) => this._handleSingleObject(textObject)); + + if ((resultArray as ArrayValueObject).getRowCount() === 1 && (resultArray as ArrayValueObject).getColumnCount() === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; } - return this._handleSingleText(text); + return this._handleSingleObject(text); } - private _handleSingleText(text: BaseValueObject) { + private _handleSingleObject(text: BaseValueObject): BaseValueObject { if (text.isError()) { return text; } @@ -47,11 +47,8 @@ export class Lower extends BaseFunction { return StringValueObject.create(''); } - if (text.isString() || text.isBoolean() || text.isNumber()) { - const textValue = text.getValue().toString().toLowerCase(); - return StringValueObject.create(textValue); - } + const result = `${text.getValue()}`.toLocaleLowerCase(); - return ErrorValueObject.create(ErrorType.VALUE); + return StringValueObject.create(result); } } diff --git a/packages/engine-formula/src/functions/text/mid/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/mid/__test__/index.spec.ts index 2036d660d64..0dfec7575fb 100644 --- a/packages/engine-formula/src/functions/text/mid/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/mid/__test__/index.spec.ts @@ -17,187 +17,112 @@ import { describe, expect, it } from 'vitest'; import { ErrorType } from '../../../../basics/error-type'; -import { ArrayValueObject, transformToValue, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; import { FUNCTION_NAMES_TEXT } from '../../function-names'; import { Mid } from '../index'; describe('Test mid function', () => { - const midFunction = new Mid(FUNCTION_NAMES_TEXT.MID); + const testFunction = new Mid(FUNCTION_NAMES_TEXT.MID); describe('Mid', () => { - it('Extract substring from single cell', () => { - const withinText = StringValueObject.create('Hello Univer'); - const startNum = NumberValueObject.create(7); - const numChars = NumberValueObject.create(6); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['Univer']]); + it('Value is normal', () => { + const text = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(3); + const result = testFunction.calculate(text, startNum, numChars); + expect(getObjectValue(result)).toStrictEqual('Uni'); }); - it('Extract substring with start position beyond string length', () => { - const withinText = StringValueObject.create('Hello'); - const startNum = NumberValueObject.create(10); - const numChars = NumberValueObject.create(5); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['']]); - }); + it('StartNum value test', () => { + const text = StringValueObject.create('Univer'); + const startNum = NullValueObject.create(); + const numChars = NumberValueObject.create(3); + const result = testFunction.calculate(text, startNum, numChars); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); - it('Extract substring with blank cell', () => { - const withinText = NullValueObject.create(); - const startNum = NumberValueObject.create(7); - const numChars = NumberValueObject.create(8); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['']]); - }); + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(text, startNum2, numChars); + expect(getObjectValue(result2)).toStrictEqual('Uni'); - it('Extract substring with boolean', () => { - const withinText = BooleanValueObject.create(true); - const startNum = NumberValueObject.create(2); - const numChars = NumberValueObject.create(1); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['R']]); - }); + const startNum3 = NumberValueObject.create(7); + const result3 = testFunction.calculate(text, startNum3, numChars); + expect(getObjectValue(result3)).toStrictEqual(''); - it('Extract substring with number', () => { - const withinText = NumberValueObject.create(12345); - const startNum = NumberValueObject.create(2); - const numChars = NumberValueObject.create(2); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['23']]); - }); + const startNum4 = StringValueObject.create('test'); + const result4 = testFunction.calculate(text, startNum4, numChars); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.VALUE); - it('Extract substring with error', () => { - const withinText = ErrorValueObject.create(ErrorType.NAME); - const startNum = NumberValueObject.create(2); - const numChars = NumberValueObject.create(2); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(result.getValue()).toBe(ErrorType.NAME); + const startNum5 = ErrorValueObject.create(ErrorType.NAME); + const result5 = testFunction.calculate(text, startNum5, numChars); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.NAME); }); - it('Extract substring with numChars exceeding string length', () => { - const withinText = StringValueObject.create('Hello'); + it('NumChars value test', () => { + const text = StringValueObject.create('Univer'); const startNum = NumberValueObject.create(1); - const numChars = NumberValueObject.create(10); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['Hello']]); + const numChars = NumberValueObject.create(-2); + const result = testFunction.calculate(text, startNum, numChars); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const numChars2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(text, startNum, numChars2); + expect(getObjectValue(result2)).toStrictEqual(''); + + const numChars3 = NumberValueObject.create(6); + const result3 = testFunction.calculate(text, startNum, numChars3); + expect(getObjectValue(result3)).toStrictEqual('Univer'); + + const numChars4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(text, startNum, numChars4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const numChars5 = StringValueObject.create('test'); + const result5 = testFunction.calculate(text, startNum, numChars5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); }); - it('Extract substring in array', () => { - const withinText = new ArrayValueObject({ + it('Value is array', () => { + const text = ArrayValueObject.create({ calculateValueList: transformToValueObject([ - ['Hello Univer'], - ['Hello World'], - ['This is a Test'], + [1, ' ', '中文测试', true, false, null], + [0, '100', '2.34', '2-way street', -3, ErrorType.NAME], ]), - rowCount: 3, - columnCount: 1, + rowCount: 2, + columnCount: 6, unitId: '', sheetId: '', row: 0, column: 0, }); - const startNum = new ArrayValueObject({ - calculateValueList: transformToValueObject([ - [7], - [7], - [6], - ]), - rowCount: 3, - columnCount: 1, - unitId: '', - sheetId: '', - row: 0, - column: 0, - }); - const numChars = new ArrayValueObject({ - calculateValueList: transformToValueObject([ - [5], - [5], - [5], - ]), - rowCount: 3, - columnCount: 1, - unitId: '', - sheetId: '', - row: 0, - column: 0, - }); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([ - ['Unive'], - ['World'], - ['is a '], + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(2); + const result = testFunction.calculate(text, startNum, numChars); + expect(getObjectValue(result)).toStrictEqual([ + ['1', ' ', '中文', 'TR', 'FA', ''], + ['0', '10', '2.', '2-', '-3', ErrorType.NAME], ]); }); - it('Extract substring with array start positions and lengths', () => { - const withinText = new ArrayValueObject({ - calculateValueList: transformToValueObject([[1, ' ', 1.23, true, false, null, 0, '100', '2.34', 'test', -3, ErrorType.NAME]]), - rowCount: 1, - columnCount: 12, - unitId: '', - sheetId: '', - row: 0, - column: 0, - }); - const startNum = new ArrayValueObject({ - calculateValueList: transformToValueObject([ - [1], - [' '], - [1.23], - [true], - [false], - [null], - [0], - ['100'], - ['2.34'], - ['test'], - [-3], - [ErrorType.NAME], - ]), - rowCount: 12, - columnCount: 1, - unitId: '', - sheetId: '', - row: 0, - column: 0, - }); - const numChars = NumberValueObject.create(1); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['1', ' ', '1', 'T', 'F', '', '0', '1', '2', 't', '-', ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], ['1', ' ', '1', 'T', 'F', '', '0', '1', '2', 't', '-', ErrorType.NAME], ['1', ' ', '1', 'T', 'F', '', '0', '1', '2', 't', '-', ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], ['', '', '', '', '', '', '', '', '', '', '', ErrorType.NAME], ['', '', '.', 'R', 'A', '', '', '0', '.', 'e', '3', ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.NAME], [ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME, ErrorType.NAME]]); - }); + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(4); + const result = testFunction.calculate(text, startNum, numChars); + expect(getObjectValue(result)).toStrictEqual(',。、;'); - it('Extract substring with emoji', () => { - const withinText = StringValueObject.create('Hello😊World'); - const startNum = NumberValueObject.create(6); - const numChars = NumberValueObject.create(2); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([['😊']]); - }); + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const numChars2 = ArrayValueObject.create('{3,5,7,10,15}'); + const result2 = testFunction.calculate(text2, startNum, numChars2); + expect(getObjectValue(result2)).toStrictEqual([ + ['Hel', 'Hello', 'Hello中文', 'Hello中文o😊', 'Hello中文o😊Wo😊r'], + ]); - it('Extract substring with invalid start position', () => { - const withinText = StringValueObject.create('Hello World'); - const startNum = new ArrayValueObject({ - calculateValueList: transformToValueObject([[-1, 0, 0.5, 1]]), - rowCount: 1, - columnCount: 4, - unitId: '', - sheetId: '', - row: 0, - column: 0, - }); - const numChars = new ArrayValueObject({ - calculateValueList: transformToValueObject([[-1], [0], [0.5], [1]]), - rowCount: 4, - columnCount: 1, - unitId: '', - sheetId: '', - row: 0, - column: 0, - }); - const result = midFunction.calculate(withinText, startNum, numChars); - expect(transformToValue(result.getArrayValue())).toStrictEqual([[ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ''], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, ''], [ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, 'H']]); + const text3 = StringValueObject.create('2012-2-2'); + const result3 = testFunction.calculate(text3, startNum, numChars); + expect(getObjectValue(result3)).toStrictEqual('2012'); }); }); }); diff --git a/packages/engine-formula/src/functions/text/mid/index.ts b/packages/engine-formula/src/functions/text/mid/index.ts index c1edc0df120..4defbc4bf3a 100644 --- a/packages/engine-formula/src/functions/text/mid/index.ts +++ b/packages/engine-formula/src/functions/text/mid/index.ts @@ -14,102 +14,87 @@ * limitations under the License. */ +import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; -import { NullValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; +import { StringValueObject } from '../../../engine/value-object/primitive-object'; import { BaseFunction } from '../../base-function'; -import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; export class Mid extends BaseFunction { override minParams = 3; override maxParams = 3; - override calculate(withinText: BaseValueObject, startNum: BaseValueObject, numChars: BaseValueObject) { - if (withinText.isError()) { - return withinText; - } - - if (startNum.isError()) { - return startNum; - } - - if (numChars.isError()) { - return numChars; - } - - // get max row length + override calculate(text: BaseValueObject, startNum: BaseValueObject, numChars: BaseValueObject): BaseValueObject { const maxRowLength = Math.max( - withinText.isArray() ? (withinText as ArrayValueObject).getRowCount() : 1, + text.isArray() ? (text as ArrayValueObject).getRowCount() : 1, startNum.isArray() ? (startNum as ArrayValueObject).getRowCount() : 1, numChars.isArray() ? (numChars as ArrayValueObject).getRowCount() : 1 ); - // get max column length const maxColumnLength = Math.max( - withinText.isArray() ? (withinText as ArrayValueObject).getColumnCount() : 1, + text.isArray() ? (text as ArrayValueObject).getColumnCount() : 1, startNum.isArray() ? (startNum as ArrayValueObject).getColumnCount() : 1, numChars.isArray() ? (numChars as ArrayValueObject).getColumnCount() : 1 ); - const withinTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, withinText); - const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, startNum); - const numCharsArray = expandArrayValueObject(maxRowLength, maxColumnLength, numChars); + const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, startNum, ErrorValueObject.create(ErrorType.NA)); + const numCharsArray = expandArrayValueObject(maxRowLength, maxColumnLength, numChars, ErrorValueObject.create(ErrorType.NA)); - return withinTextArray.map((withinTextValue, rowIndex, columnIndex) => { - return this._handleSingleText(withinTextValue, rowIndex, columnIndex, startNumArray, numCharsArray); - }); - } + const resultArray = textArray.mapValue((textObject, rowIndex, columnIndex) => { + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + const numCharsObject = numCharsArray.get(rowIndex, columnIndex) as BaseValueObject; - private _handleSingleText(withinTextValue: BaseValueObject, rowIndex: number, columnIndex: number, startNumArray: ArrayValueObject, numCharsArray: ArrayValueObject) { - let startNumValue = startNumArray.get(rowIndex, columnIndex) || NullValueObject.create(); - let numCharsValue = numCharsArray.get(rowIndex, columnIndex) || NullValueObject.create(); + if (textObject.isError()) { + return textObject; + } - if (startNumValue.isError()) { - return startNumValue; - } + if (startNumObject.isError()) { + return startNumObject; + } - if (numCharsValue.isError()) { - return numCharsValue; - } + if (numCharsObject.isError()) { + return numCharsObject; + } - let withinTextValueString = withinTextValue.getValue(); + return this._handleSingleObject(textObject, startNumObject, numCharsObject); + }); - if (withinTextValue.isNull()) { - withinTextValueString = ''; + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; } - if (withinTextValue.isBoolean()) { - withinTextValueString = withinTextValueString ? 'TRUE' : 'FALSE'; - } + return resultArray; + } - withinTextValueString = `${withinTextValueString}`; + private _handleSingleObject(text: BaseValueObject, startNum: BaseValueObject, numChars: BaseValueObject): BaseValueObject { + const textValue = getTextValueOfNumberFormat(text); - if (startNumValue.isString() || startNumValue.isBoolean() || startNumValue.isNull()) { - startNumValue = startNumValue.convertToNumberObjectValue(); - } + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum, numChars); - if (startNumValue.isError()) { - return startNumValue; + if (isError) { + return errorObject as BaseValueObject; } - if (numCharsValue.isString() || numCharsValue.isBoolean() || numCharsValue.isNull()) { - numCharsValue = numCharsValue.convertToNumberObjectValue(); - } + const [startNumObject, numCharsObject] = variants as BaseValueObject[]; - if (numCharsValue.isError()) { - return numCharsValue; - } + const startNumValue = Math.floor(+startNumObject.getValue()); + const numCharsValue = Math.floor(+numCharsObject.getValue()); - const startNumValueNumber = Math.floor(+startNumValue.getValue()) - 1; - const numCharsValueNumber = numCharsValue.getValue() as number; - - if (startNumValueNumber < 0 || numCharsValueNumber < 0) { + if (startNumValue <= 0 || numCharsValue < 0) { return ErrorValueObject.create(ErrorType.VALUE); } - return StringValueObject.create(withinTextValueString.substring(startNumValueNumber, startNumValueNumber + numCharsValueNumber)); + if (text.isNull() || startNumValue > textValue.length || numCharsValue === 0) { + return StringValueObject.create(''); + } + + const result = textValue.substring(startNumValue - 1, startNumValue - 1 + numCharsValue); + + return StringValueObject.create(result); } } - diff --git a/packages/engine-formula/src/functions/text/midb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/midb/__test__/index.spec.ts new file mode 100644 index 00000000000..3bb242172b1 --- /dev/null +++ b/packages/engine-formula/src/functions/text/midb/__test__/index.spec.ts @@ -0,0 +1,128 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Midb } from '../index'; + +describe('Test midb function', () => { + const testFunction = new Midb(FUNCTION_NAMES_TEXT.MIDB); + + describe('Midb', () => { + it('Value is normal', () => { + const text = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(3); + const result = testFunction.calculate(text, startNum, numBytes); + expect(getObjectValue(result)).toStrictEqual('Uni'); + }); + + it('StartNum value test', () => { + const text = StringValueObject.create('Univer'); + const startNum = NullValueObject.create(); + const numBytes = NumberValueObject.create(3); + const result = testFunction.calculate(text, startNum, numBytes); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(text, startNum2, numBytes); + expect(getObjectValue(result2)).toStrictEqual('Uni'); + + const startNum3 = NumberValueObject.create(7); + const result3 = testFunction.calculate(text, startNum3, numBytes); + expect(getObjectValue(result3)).toStrictEqual(''); + + const startNum4 = StringValueObject.create('test'); + const result4 = testFunction.calculate(text, startNum4, numBytes); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.VALUE); + + const startNum5 = ErrorValueObject.create(ErrorType.NAME); + const result5 = testFunction.calculate(text, startNum5, numBytes); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.NAME); + }); + + it('NumBytes value test', () => { + const text = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(-2); + const result = testFunction.calculate(text, startNum, numBytes); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const numBytes2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(text, startNum, numBytes2); + expect(getObjectValue(result2)).toStrictEqual(''); + + const numBytes3 = NumberValueObject.create(6); + const result3 = testFunction.calculate(text, startNum, numBytes3); + expect(getObjectValue(result3)).toStrictEqual('Univer'); + + const numBytes4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(text, startNum, numBytes4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const numBytes5 = StringValueObject.create('test'); + const result5 = testFunction.calculate(text, startNum, numBytes5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('Value is array', () => { + const text = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, '100', '2.34', '2-way street', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(2); + const result = testFunction.calculate(text, startNum, numBytes); + expect(getObjectValue(result)).toStrictEqual([ + ['1', ' ', '中', 'TR', 'FA', ''], + ['0', '10', '2.', '2-', '-3', ErrorType.NAME], + ]); + }); + + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(4); + const result = testFunction.calculate(text, startNum, numBytes); + expect(getObjectValue(result)).toStrictEqual(',。、'); + + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const numBytes2 = ArrayValueObject.create('{3,5,7,10,15}'); + const result2 = testFunction.calculate(text2, startNum, numBytes2); + expect(getObjectValue(result2)).toStrictEqual([ + ['Hel', 'Hello', 'Hello中', 'Hello中文o', 'Hello中文o😊W'], + ]); + + const text3 = StringValueObject.create('2012-2-2'); + const result3 = testFunction.calculate(text3, startNum, numBytes); + expect(getObjectValue(result3)).toStrictEqual('2012'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/midb/index.ts b/packages/engine-formula/src/functions/text/midb/index.ts new file mode 100644 index 00000000000..7a5396c109b --- /dev/null +++ b/packages/engine-formula/src/functions/text/midb/index.ts @@ -0,0 +1,111 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { getCharLenByteInText } from '../../../engine/utils/char-kit'; +import { checkVariantsErrorIsStringToNumber } 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 Midb extends BaseFunction { + override minParams = 3; + + override maxParams = 3; + + override calculate(text: BaseValueObject, startNum: BaseValueObject, numBytes: BaseValueObject): BaseValueObject { + const maxRowLength = Math.max( + text.isArray() ? (text as ArrayValueObject).getRowCount() : 1, + startNum.isArray() ? (startNum as ArrayValueObject).getRowCount() : 1, + numBytes.isArray() ? (numBytes as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + text.isArray() ? (text as ArrayValueObject).getColumnCount() : 1, + startNum.isArray() ? (startNum as ArrayValueObject).getColumnCount() : 1, + numBytes.isArray() ? (numBytes as ArrayValueObject).getColumnCount() : 1 + ); + + const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, startNum, ErrorValueObject.create(ErrorType.NA)); + const numBytesArray = expandArrayValueObject(maxRowLength, maxColumnLength, numBytes, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = textArray.mapValue((textObject, rowIndex, columnIndex) => { + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + const numBytesObject = numBytesArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (textObject.isError()) { + return textObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + if (numBytesObject.isError()) { + return numBytesObject; + } + + return this._handleSingleObject(textObject, startNumObject, numBytesObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(text: BaseValueObject, startNum: BaseValueObject, numBytes: BaseValueObject): BaseValueObject { + let textValue = getTextValueOfNumberFormat(text); + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum, numBytes); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject, numBytesObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + const numBytesValue = Math.floor(+numBytesObject.getValue()); + + if (startNumValue <= 0 || numBytesValue < 0) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + if (text.isNull() || startNumValue > textValue.length || numBytesValue === 0) { + return StringValueObject.create(''); + } + + textValue = textValue.substring(startNumValue - 1); + + let index = 0; + let lenByte = 0; + let result = ''; + + while (lenByte < numBytesValue && index < textValue.length) { + lenByte += getCharLenByteInText(textValue, index); + result += textValue.charAt(index); + index++; + } + + return StringValueObject.create(result); + } +} diff --git a/packages/engine-formula/src/functions/text/replace/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/replace/__test__/index.spec.ts new file mode 100644 index 00000000000..f753e9524d6 --- /dev/null +++ b/packages/engine-formula/src/functions/text/replace/__test__/index.spec.ts @@ -0,0 +1,151 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Replace } from '../index'; + +describe('Test replace function', () => { + const testFunction = new Replace(FUNCTION_NAMES_TEXT.REPLACE); + + describe('Replace', () => { + it('Value is normal', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(0); + const newText = StringValueObject.create('Hello '); + const result = testFunction.calculate(oldText, startNum, numChars, newText); + expect(getObjectValue(result)).toStrictEqual('Hello Univer'); + }); + + it('StartNum value test', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NullValueObject.create(); + const numChars = NumberValueObject.create(3); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numChars, newText); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(oldText, startNum2, numChars, newText); + expect(getObjectValue(result2)).toStrictEqual('~ver'); + + const startNum3 = NumberValueObject.create(7); + const result3 = testFunction.calculate(oldText, startNum3, numChars, newText); + expect(getObjectValue(result3)).toStrictEqual('Univer~'); + + const startNum4 = StringValueObject.create('test'); + const result4 = testFunction.calculate(oldText, startNum4, numChars, newText); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.VALUE); + + const startNum5 = ErrorValueObject.create(ErrorType.NAME); + const result5 = testFunction.calculate(oldText, startNum5, numChars, newText); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.NAME); + }); + + it('NumChars value test', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(-2); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numChars, newText); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const numChars2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(oldText, startNum, numChars2, newText); + expect(getObjectValue(result2)).toStrictEqual('~Univer'); + + const numChars3 = NumberValueObject.create(6); + const result3 = testFunction.calculate(oldText, startNum, numChars3, newText); + expect(getObjectValue(result3)).toStrictEqual('~'); + + const numChars4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(oldText, startNum, numChars4, newText); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const numChars5 = StringValueObject.create('test'); + const result5 = testFunction.calculate(oldText, startNum, numChars5, newText); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('NewText value test', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(2); + const newText = NullValueObject.create(); + const result = testFunction.calculate(oldText, startNum, numChars, newText); + expect(getObjectValue(result)).toStrictEqual('iver'); + + const newText2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(oldText, startNum, numChars, newText2); + expect(getObjectValue(result2)).toStrictEqual('TRUEiver'); + + const newText3 = ErrorValueObject.create(ErrorType.NAME); + const result3 = testFunction.calculate(oldText, startNum, numChars, newText3); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.NAME); + }); + + it('Value is array', () => { + const oldText = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, '2012-2-2', '2.34', '2-way street', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(2); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numChars, newText); + expect(getObjectValue(result)).toStrictEqual([ + ['~', '~', '~测试', '~UE', '~LSE', '~'], + ['~', '~12-2-2', '~34', '~way street', '~', ErrorType.NAME], + ]); + }); + + it('More test', () => { + const oldText = StringValueObject.create(',。、;:{}'); + const startNum = NumberValueObject.create(1); + const numChars = NumberValueObject.create(4); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numChars, newText); + expect(getObjectValue(result)).toStrictEqual('~:{}'); + + const oldText2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const numChars2 = ArrayValueObject.create('{3,5,7,10,15}'); + const result2 = testFunction.calculate(oldText2, startNum, numChars2, newText); + expect(getObjectValue(result2)).toStrictEqual([ + ['~lo中文o😊Wo😊rld', '~中文o😊Wo😊rld', '~o😊Wo😊rld', '~Wo😊rld', '~ld'], + ]); + + const oldText3 = NumberValueObject.create(0.01, '0%'); + const numChars3 = NumberValueObject.create(1); + const result3 = testFunction.calculate(oldText3, startNum, numChars3, newText); + expect(getObjectValue(result3)).toStrictEqual('~%'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/replace/index.ts b/packages/engine-formula/src/functions/text/replace/index.ts new file mode 100644 index 00000000000..8472db33d3c --- /dev/null +++ b/packages/engine-formula/src/functions/text/replace/index.ts @@ -0,0 +1,105 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { checkVariantsErrorIsStringToNumber } 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 Replace extends BaseFunction { + override minParams = 4; + + override maxParams = 4; + + override calculate(oldText: BaseValueObject, startNum: BaseValueObject, numChars: BaseValueObject, newText: BaseValueObject): BaseValueObject { + const maxRowLength = Math.max( + oldText.isArray() ? (oldText as ArrayValueObject).getRowCount() : 1, + startNum.isArray() ? (startNum as ArrayValueObject).getRowCount() : 1, + numChars.isArray() ? (numChars as ArrayValueObject).getRowCount() : 1, + newText.isArray() ? (newText as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + oldText.isArray() ? (oldText as ArrayValueObject).getColumnCount() : 1, + startNum.isArray() ? (startNum as ArrayValueObject).getColumnCount() : 1, + numChars.isArray() ? (numChars as ArrayValueObject).getColumnCount() : 1, + newText.isArray() ? (newText as ArrayValueObject).getColumnCount() : 1 + ); + + const oldTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, oldText, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, startNum, ErrorValueObject.create(ErrorType.NA)); + const numCharsArray = expandArrayValueObject(maxRowLength, maxColumnLength, numChars, ErrorValueObject.create(ErrorType.NA)); + const newTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, newText, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = oldTextArray.mapValue((oldTextObject, rowIndex, columnIndex) => { + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + const numCharsObject = numCharsArray.get(rowIndex, columnIndex) as BaseValueObject; + const newTextObject = newTextArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (oldTextObject.isError()) { + return oldTextObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + if (numCharsObject.isError()) { + return numCharsObject; + } + + if (newTextObject.isError()) { + return newTextObject; + } + + return this._handleSingleObject(oldTextObject, startNumObject, numCharsObject, newTextObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(oldText: BaseValueObject, startNum: BaseValueObject, numChars: BaseValueObject, newText: BaseValueObject): BaseValueObject { + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum, numChars); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject, numCharsObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + const numCharsValue = Math.floor(+numCharsObject.getValue()); + + if (startNumValue <= 0 || numCharsValue < 0) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + const oldTextValue = getTextValueOfNumberFormat(oldText); + const newTextValue = getTextValueOfNumberFormat(newText); + + const result = oldTextValue.substring(0, startNumValue - 1) + newTextValue + oldTextValue.substring(startNumValue - 1 + numCharsValue); + + return StringValueObject.create(result); + } +} diff --git a/packages/engine-formula/src/functions/text/replaceb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/replaceb/__test__/index.spec.ts new file mode 100644 index 00000000000..fd1f980efc0 --- /dev/null +++ b/packages/engine-formula/src/functions/text/replaceb/__test__/index.spec.ts @@ -0,0 +1,151 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Replaceb } from '../index'; + +describe('Test replaceb function', () => { + const testFunction = new Replaceb(FUNCTION_NAMES_TEXT.REPLACEB); + + describe('Replaceb', () => { + it('Value is normal', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(0); + const newText = StringValueObject.create('Hello '); + const result = testFunction.calculate(oldText, startNum, numBytes, newText); + expect(getObjectValue(result)).toStrictEqual('Hello Univer'); + }); + + it('StartNum value test', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NullValueObject.create(); + const numBytes = NumberValueObject.create(3); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numBytes, newText); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(oldText, startNum2, numBytes, newText); + expect(getObjectValue(result2)).toStrictEqual('~ver'); + + const startNum3 = NumberValueObject.create(7); + const result3 = testFunction.calculate(oldText, startNum3, numBytes, newText); + expect(getObjectValue(result3)).toStrictEqual('Univer~'); + + const startNum4 = StringValueObject.create('test'); + const result4 = testFunction.calculate(oldText, startNum4, numBytes, newText); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.VALUE); + + const startNum5 = ErrorValueObject.create(ErrorType.NAME); + const result5 = testFunction.calculate(oldText, startNum5, numBytes, newText); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.NAME); + }); + + it('NumBytes value test', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(-2); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numBytes, newText); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const numBytes2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(oldText, startNum, numBytes2, newText); + expect(getObjectValue(result2)).toStrictEqual('~Univer'); + + const numBytes3 = NumberValueObject.create(6); + const result3 = testFunction.calculate(oldText, startNum, numBytes3, newText); + expect(getObjectValue(result3)).toStrictEqual('~'); + + const numBytes4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(oldText, startNum, numBytes4, newText); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const numBytes5 = StringValueObject.create('test'); + const result5 = testFunction.calculate(oldText, startNum, numBytes5, newText); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('NewText value test', () => { + const oldText = StringValueObject.create('Univer'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(2); + const newText = NullValueObject.create(); + const result = testFunction.calculate(oldText, startNum, numBytes, newText); + expect(getObjectValue(result)).toStrictEqual('iver'); + + const newText2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(oldText, startNum, numBytes, newText2); + expect(getObjectValue(result2)).toStrictEqual('TRUEiver'); + + const newText3 = ErrorValueObject.create(ErrorType.NAME); + const result3 = testFunction.calculate(oldText, startNum, numBytes, newText3); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.NAME); + }); + + it('Value is array', () => { + const oldText = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, '2012-2-2', '2.34', '2-way street', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(2); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numBytes, newText); + expect(getObjectValue(result)).toStrictEqual([ + ['~', '~', '~文测试', '~UE', '~LSE', '~'], + ['~', '~12-2-2', '~34', '~way street', '~', ErrorType.NAME], + ]); + }); + + it('More test', () => { + const oldText = StringValueObject.create(',。、;:{}'); + const startNum = NumberValueObject.create(1); + const numBytes = NumberValueObject.create(4); + const newText = StringValueObject.create('~'); + const result = testFunction.calculate(oldText, startNum, numBytes, newText); + expect(getObjectValue(result)).toStrictEqual('~;:{}'); + + const oldText2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const numBytes2 = ArrayValueObject.create('{3,5,7,10,15}'); + const result2 = testFunction.calculate(oldText2, startNum, numBytes2, newText); + expect(getObjectValue(result2)).toStrictEqual([ + ['~lo中文o😊Wo😊rld', '~中文o😊Wo😊rld', '~文o😊Wo😊rld', '~😊Wo😊rld', '~o😊rld'], + ]); + + const oldText3 = NumberValueObject.create(0.01, '0%'); + const numChars3 = NumberValueObject.create(1); + const result3 = testFunction.calculate(oldText3, startNum, numChars3, newText); + expect(getObjectValue(result3)).toStrictEqual('~%'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/replaceb/index.ts b/packages/engine-formula/src/functions/text/replaceb/index.ts new file mode 100644 index 00000000000..cb44483029e --- /dev/null +++ b/packages/engine-formula/src/functions/text/replaceb/index.ts @@ -0,0 +1,118 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { getCharLenByteInText } from '../../../engine/utils/char-kit'; +import { checkVariantsErrorIsStringToNumber } 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 Replaceb extends BaseFunction { + override minParams = 4; + + override maxParams = 4; + + override calculate(oldText: BaseValueObject, startNum: BaseValueObject, numBytes: BaseValueObject, newText: BaseValueObject): BaseValueObject { + const maxRowLength = Math.max( + oldText.isArray() ? (oldText as ArrayValueObject).getRowCount() : 1, + startNum.isArray() ? (startNum as ArrayValueObject).getRowCount() : 1, + numBytes.isArray() ? (numBytes as ArrayValueObject).getRowCount() : 1, + newText.isArray() ? (newText as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + oldText.isArray() ? (oldText as ArrayValueObject).getColumnCount() : 1, + startNum.isArray() ? (startNum as ArrayValueObject).getColumnCount() : 1, + numBytes.isArray() ? (numBytes as ArrayValueObject).getColumnCount() : 1, + newText.isArray() ? (newText as ArrayValueObject).getColumnCount() : 1 + ); + + const oldTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, oldText, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, startNum, ErrorValueObject.create(ErrorType.NA)); + const numBytesArray = expandArrayValueObject(maxRowLength, maxColumnLength, numBytes, ErrorValueObject.create(ErrorType.NA)); + const newTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, newText, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = oldTextArray.mapValue((oldTextObject, rowIndex, columnIndex) => { + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + const numBytesObject = numBytesArray.get(rowIndex, columnIndex) as BaseValueObject; + const newTextObject = newTextArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (oldTextObject.isError()) { + return oldTextObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + if (numBytesObject.isError()) { + return numBytesObject; + } + + if (newTextObject.isError()) { + return newTextObject; + } + + return this._handleSingleObject(oldTextObject, startNumObject, numBytesObject, newTextObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(oldText: BaseValueObject, startNum: BaseValueObject, numBytes: BaseValueObject, newText: BaseValueObject): BaseValueObject { + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum, numBytes); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject, numBytesObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + const numBytesValue = Math.floor(+numBytesObject.getValue()); + + if (startNumValue <= 0 || numBytesValue < 0) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + let oldTextValue = getTextValueOfNumberFormat(oldText); + const newTextValue = getTextValueOfNumberFormat(newText); + + let result = oldTextValue.substring(0, startNumValue - 1); + + oldTextValue = oldTextValue.substring(startNumValue - 1); + + let index = 0; + let lenByte = 0; + + while (lenByte < numBytesValue && index < oldTextValue.length) { + lenByte += getCharLenByteInText(oldTextValue, index); + index++; + } + + result += newTextValue + oldTextValue.substring(index); + + return StringValueObject.create(result); + } +} diff --git a/packages/engine-formula/src/functions/text/right/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/right/__test__/index.spec.ts index 026c70bc779..2cb1fe0c762 100644 --- a/packages/engine-formula/src/functions/text/right/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/right/__test__/index.spec.ts @@ -61,7 +61,7 @@ describe('Test right function', () => { const text = ArrayValueObject.create({ calculateValueList: transformToValueObject([ [1, ' ', '中文测试', true, false, null], - [0, '100', '2.34', '2-way street', -3, ErrorType.NAME], + [0, '2012-2-2', '2.34', '2-way street', -3, ErrorType.NAME], ]), rowCount: 2, columnCount: 6, @@ -74,7 +74,7 @@ describe('Test right function', () => { const result = testFunction.calculate(text, numChars); expect(getObjectValue(result)).toStrictEqual([ ['1', ' ', '测试', 'UE', 'SE', ''], - ['0', '00', '34', 'et', '-3', ErrorType.NAME], + ['0', '-2', '34', 'et', '-3', ErrorType.NAME], ]); }); @@ -90,6 +90,11 @@ describe('Test right function', () => { expect(getObjectValue(result2)).toStrictEqual([ ['rld', '😊rld', 'Wo😊rld', '😊Wo😊rld', 'llo中文o😊Wo😊rld'], ]); + + const text3 = NumberValueObject.create(0.01, '0%'); + const numChars3 = NumberValueObject.create(2); + const result3 = testFunction.calculate(text3, numChars3); + expect(getObjectValue(result3)).toStrictEqual('1%'); }); }); }); diff --git a/packages/engine-formula/src/functions/text/right/index.ts b/packages/engine-formula/src/functions/text/right/index.ts index 2571505228d..2dee80ec09d 100644 --- a/packages/engine-formula/src/functions/text/right/index.ts +++ b/packages/engine-formula/src/functions/text/right/index.ts @@ -16,10 +16,11 @@ import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; import { expandArrayValueObject } from '../../../engine/utils/array-object'; import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; -import { NullValueObject, NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; +import { NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; import { BaseFunction } from '../../base-function'; export class Right extends BaseFunction { @@ -40,8 +41,8 @@ export class Right extends BaseFunction { _numChars.isArray() ? (_numChars as ArrayValueObject).getColumnCount() : 1 ); - const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, NullValueObject.create()); - const numCharsArray = expandArrayValueObject(maxRowLength, maxColumnLength, _numChars, NullValueObject.create()); + const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, ErrorValueObject.create(ErrorType.NA)); + const numCharsArray = expandArrayValueObject(maxRowLength, maxColumnLength, _numChars, ErrorValueObject.create(ErrorType.NA)); const resultArray = textArray.mapValue((textObject, rowIndex, columnIndex) => { const numCharsObject = numCharsArray.get(rowIndex, columnIndex) as BaseValueObject; @@ -65,11 +66,7 @@ export class Right extends BaseFunction { } private _handleSingleObject(text: BaseValueObject, numChars: BaseValueObject): BaseValueObject { - let textValue = `${text.getValue()}`; - - if (text.isBoolean()) { - textValue = textValue.toLocaleUpperCase(); - } + const textValue = getTextValueOfNumberFormat(text); const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(numChars); diff --git a/packages/engine-formula/src/functions/text/rightb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/rightb/__test__/index.spec.ts index 951ac3758a9..45335a95e9a 100644 --- a/packages/engine-formula/src/functions/text/rightb/__test__/index.spec.ts +++ b/packages/engine-formula/src/functions/text/rightb/__test__/index.spec.ts @@ -61,7 +61,7 @@ describe('Test rightb function', () => { const text = ArrayValueObject.create({ calculateValueList: transformToValueObject([ [1, ' ', '中文测试', true, false, null], - [0, '100', '2.34', '2-way street', -3, ErrorType.NAME], + [0, '2012-2-2', '2.34', '2-way street', -3, ErrorType.NAME], ]), rowCount: 2, columnCount: 6, @@ -74,14 +74,14 @@ describe('Test rightb function', () => { const result = testFunction.calculate(text, numBytes); expect(getObjectValue(result)).toStrictEqual([ ['1', ' ', '试', 'UE', 'SE', ''], - ['0', '00', '34', 'et', '-3', ErrorType.NAME], + ['0', '-2', '34', 'et', '-3', ErrorType.NAME], ]); const numBytes2 = NumberValueObject.create(3); const result2 = testFunction.calculate(text, numBytes2); expect(getObjectValue(result2)).toStrictEqual([ ['1', ' ', '测试', 'RUE', 'LSE', ''], - ['0', '100', '.34', 'eet', '-3', ErrorType.NAME], + ['0', '2-2', '.34', 'eet', '-3', ErrorType.NAME], ]); }); @@ -97,6 +97,11 @@ describe('Test rightb function', () => { expect(getObjectValue(result2)).toStrictEqual([ ['rld', '😊rld', '😊rld', 'o😊rld', '文o😊Wo😊rld'], ]); + + const text3 = NumberValueObject.create(0.01, '0%'); + const numBytes3 = NumberValueObject.create(2); + const result3 = testFunction.calculate(text3, numBytes3); + expect(getObjectValue(result3)).toStrictEqual('1%'); }); }); }); diff --git a/packages/engine-formula/src/functions/text/rightb/index.ts b/packages/engine-formula/src/functions/text/rightb/index.ts index cf99ab0f0a7..39f12662b1a 100644 --- a/packages/engine-formula/src/functions/text/rightb/index.ts +++ b/packages/engine-formula/src/functions/text/rightb/index.ts @@ -16,11 +16,12 @@ import type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; import { expandArrayValueObject } from '../../../engine/utils/array-object'; import { getCharLenByteInText } from '../../../engine/utils/char-kit'; import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; -import { NullValueObject, NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; +import { NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; import { BaseFunction } from '../../base-function'; export class Rightb extends BaseFunction { @@ -41,8 +42,8 @@ export class Rightb extends BaseFunction { _numBytes.isArray() ? (_numBytes as ArrayValueObject).getColumnCount() : 1 ); - const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, NullValueObject.create()); - const numBytesArray = expandArrayValueObject(maxRowLength, maxColumnLength, _numBytes, NullValueObject.create()); + const textArray = expandArrayValueObject(maxRowLength, maxColumnLength, text, ErrorValueObject.create(ErrorType.NA)); + const numBytesArray = expandArrayValueObject(maxRowLength, maxColumnLength, _numBytes, ErrorValueObject.create(ErrorType.NA)); const resultArray = textArray.mapValue((textObject, rowIndex, columnIndex) => { const numBytesObject = numBytesArray.get(rowIndex, columnIndex) as BaseValueObject; @@ -66,11 +67,7 @@ export class Rightb extends BaseFunction { } private _handleSingleObject(text: BaseValueObject, numBytes: BaseValueObject): BaseValueObject { - let textValue = `${text.getValue()}`; - - if (text.isBoolean()) { - textValue = textValue.toLocaleUpperCase(); - } + const textValue = getTextValueOfNumberFormat(text); const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(numBytes); diff --git a/packages/engine-formula/src/functions/text/search/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/search/__test__/index.spec.ts new file mode 100644 index 00000000000..2710b81e089 --- /dev/null +++ b/packages/engine-formula/src/functions/text/search/__test__/index.spec.ts @@ -0,0 +1,143 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Search } from '../index'; + +describe('Test search function', () => { + const testFunction = new Search(FUNCTION_NAMES_TEXT.SEARCH); + + describe('Search', () => { + it('Value is normal', () => { + const findText = StringValueObject.create('univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(7); + }); + + it('FindText value test', () => { + const findText = NullValueObject.create(); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(1); + + const findText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText2, withinText, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const findText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText3, withinText, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const findText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText4, withinText, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('WithinText value test', () => { + const findText = StringValueObject.create('Univer'); + const withinText = NullValueObject.create(); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const withinText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText, withinText2, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const withinText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText3, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const withinText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText4, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('StartNum value test', () => { + const findText = StringValueObject.create('univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NullValueObject.create(); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(findText, withinText, startNum2); + expect(getObjectValue(result2)).toStrictEqual(7); + + const startNum3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText, startNum3); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const startNum4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText, startNum4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const startNum5 = NumberValueObject.create(13); + const result5 = testFunction.calculate(findText, withinText, startNum5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('Value is array', () => { + const findText = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, 'm', '2.34', 'M', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const withinText = StringValueObject.create('Miriam McGovern'); + const startNum = NumberValueObject.create(2); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [ErrorType.VALUE, 7, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, 2], + [ErrorType.VALUE, 6, ErrorType.VALUE, 6, ErrorType.VALUE, ErrorType.NAME], + ]); + }); + + it('More test', () => { + const findText = StringValueObject.create('O'); + const withinText = StringValueObject.create('Hello中文o😊Wo😊rld'); + const startNum = ArrayValueObject.create('{1,6,9,13}'); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [5, 8, 12, ErrorType.VALUE], + ]); + + const findText2 = StringValueObject.create('2'); + const withinText2 = StringValueObject.create('2012-2-2'); + const startNum2 = ArrayValueObject.create('{1,2,5,7}'); + const result2 = testFunction.calculate(findText2, withinText2, startNum2); + expect(getObjectValue(result2)).toStrictEqual([ + [1, 4, 6, 8], + ]); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/search/index.ts b/packages/engine-formula/src/functions/text/search/index.ts new file mode 100644 index 00000000000..83d1c5d4563 --- /dev/null +++ b/packages/engine-formula/src/functions/text/search/index.ts @@ -0,0 +1,106 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; +import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import { NumberValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Search extends BaseFunction { + override minParams = 2; + + override maxParams = 3; + + override calculate(findText: BaseValueObject, withinText: BaseValueObject, startNum?: BaseValueObject): BaseValueObject { + const _startNum = startNum ?? NumberValueObject.create(1); + + const maxRowLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getRowCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getRowCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getColumnCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getColumnCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getColumnCount() : 1 + ); + + const findTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, findText, ErrorValueObject.create(ErrorType.NA)); + const withinTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, withinText, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, _startNum, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = findTextArray.mapValue((findTextObject, rowIndex, columnIndex) => { + const withinTextObject = withinTextArray.get(rowIndex, columnIndex) as BaseValueObject; + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (findTextObject.isError()) { + return findTextObject; + } + + if (withinTextObject.isError()) { + return withinTextObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + return this._handleSingleObject(findTextObject, withinTextObject, startNumObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(findText: BaseValueObject, withinText: BaseValueObject, startNum: BaseValueObject): BaseValueObject { + const findTextValue = getTextValueOfNumberFormat(findText).toLocaleUpperCase(); + const withinTextValue = getTextValueOfNumberFormat(withinText).toLocaleUpperCase(); + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + + if (withinText.isNull() || startNumValue <= 0 || startNumValue > withinTextValue.length) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + if (findText.isNull() || findTextValue.length === 0) { + return NumberValueObject.create(startNumValue); + } + + const result = withinTextValue.indexOf(findTextValue, startNumValue - 1); + + if (result === -1) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + return NumberValueObject.create(result + 1); + } +} diff --git a/packages/engine-formula/src/functions/text/searchb/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/searchb/__test__/index.spec.ts new file mode 100644 index 00000000000..666ab5deaf4 --- /dev/null +++ b/packages/engine-formula/src/functions/text/searchb/__test__/index.spec.ts @@ -0,0 +1,148 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Searchb } from '../index'; + +describe('Test searchb function', () => { + const testFunction = new Searchb(FUNCTION_NAMES_TEXT.SEARCHB); + + describe('Searchb', () => { + it('Value is normal', () => { + const findText = StringValueObject.create('univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(7); + }); + + it('FindText value test', () => { + const findText = NullValueObject.create(); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(1); + + const findText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText2, withinText, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const findText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText3, withinText, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const findText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText4, withinText, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('WithinText value test', () => { + const findText = StringValueObject.create('Univer'); + const withinText = NullValueObject.create(); + const startNum = NumberValueObject.create(1); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const withinText2 = BooleanValueObject.create(false); + const result2 = testFunction.calculate(findText, withinText2, startNum); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.VALUE); + + const withinText3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText3, startNum); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const withinText4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText4, startNum); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + }); + + it('StartNum value test', () => { + const findText = StringValueObject.create('univer'); + const withinText = StringValueObject.create('Hello Univer'); + const startNum = NullValueObject.create(); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual(ErrorType.VALUE); + + const startNum2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(findText, withinText, startNum2); + expect(getObjectValue(result2)).toStrictEqual(7); + + const startNum3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(findText, withinText, startNum3); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const startNum4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(findText, withinText, startNum4); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const startNum5 = NumberValueObject.create(13); + const result5 = testFunction.calculate(findText, withinText, startNum5); + expect(getObjectValue(result5)).toStrictEqual(ErrorType.VALUE); + }); + + it('Value is array', () => { + const findText = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, 'm', '2.34', 'M', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const withinText = StringValueObject.create('Miriam McGovern'); + const startNum = NumberValueObject.create(2); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [ErrorType.VALUE, 7, ErrorType.VALUE, ErrorType.VALUE, ErrorType.VALUE, 2], + [ErrorType.VALUE, 6, ErrorType.VALUE, 6, ErrorType.VALUE, ErrorType.NAME], + ]); + }); + + it('More test', () => { + const findText = StringValueObject.create('O'); + const withinText = StringValueObject.create('Hello中文o😊Wo😊rld'); + const startNum = ArrayValueObject.create('{1,6,9,13}'); + const result = testFunction.calculate(findText, withinText, startNum); + expect(getObjectValue(result)).toStrictEqual([ + [5, 10, 16, ErrorType.VALUE], + ]); + + const findText2 = StringValueObject.create('2'); + const withinText2 = StringValueObject.create('2012-2-2'); + const startNum2 = ArrayValueObject.create('{1,2,5,7}'); + const result2 = testFunction.calculate(findText2, withinText2, startNum2); + expect(getObjectValue(result2)).toStrictEqual([ + [1, 4, 6, 8], + ]); + + const findText3 = StringValueObject.create('欢迎'); + const withinText3 = StringValueObject.create('你好,欢迎'); + const result3 = testFunction.calculate(findText3, withinText3); + expect(getObjectValue(result3)).toStrictEqual(7); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/searchb/index.ts b/packages/engine-formula/src/functions/text/searchb/index.ts new file mode 100644 index 00000000000..37cc8c81aa4 --- /dev/null +++ b/packages/engine-formula/src/functions/text/searchb/index.ts @@ -0,0 +1,109 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import { ErrorType } from '../../../basics/error-type'; +import { getTextValueOfNumberFormat } from '../../../basics/format'; +import { expandArrayValueObject } from '../../../engine/utils/array-object'; +import { charLenByte } from '../../../engine/utils/char-kit'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; +import { type BaseValueObject, ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import { NumberValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Searchb extends BaseFunction { + override minParams = 2; + + override maxParams = 3; + + override calculate(findText: BaseValueObject, withinText: BaseValueObject, startNum?: BaseValueObject): BaseValueObject { + const _startNum = startNum ?? NumberValueObject.create(1); + + const maxRowLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getRowCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getRowCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getRowCount() : 1 + ); + + const maxColumnLength = Math.max( + findText.isArray() ? (findText as ArrayValueObject).getColumnCount() : 1, + withinText.isArray() ? (withinText as ArrayValueObject).getColumnCount() : 1, + _startNum.isArray() ? (_startNum as ArrayValueObject).getColumnCount() : 1 + ); + + const findTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, findText, ErrorValueObject.create(ErrorType.NA)); + const withinTextArray = expandArrayValueObject(maxRowLength, maxColumnLength, withinText, ErrorValueObject.create(ErrorType.NA)); + const startNumArray = expandArrayValueObject(maxRowLength, maxColumnLength, _startNum, ErrorValueObject.create(ErrorType.NA)); + + const resultArray = findTextArray.mapValue((findTextObject, rowIndex, columnIndex) => { + const withinTextObject = withinTextArray.get(rowIndex, columnIndex) as BaseValueObject; + const startNumObject = startNumArray.get(rowIndex, columnIndex) as BaseValueObject; + + if (findTextObject.isError()) { + return findTextObject; + } + + if (withinTextObject.isError()) { + return withinTextObject; + } + + if (startNumObject.isError()) { + return startNumObject; + } + + return this._handleSingleObject(findTextObject, withinTextObject, startNumObject); + }); + + if (maxRowLength === 1 && maxColumnLength === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + private _handleSingleObject(findText: BaseValueObject, withinText: BaseValueObject, startNum: BaseValueObject): BaseValueObject { + const findTextValue = getTextValueOfNumberFormat(findText).toLocaleUpperCase(); + const withinTextValue = getTextValueOfNumberFormat(withinText).toLocaleUpperCase(); + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(startNum); + + if (isError) { + return errorObject as BaseValueObject; + } + + const [startNumObject] = variants as BaseValueObject[]; + + const startNumValue = Math.floor(+startNumObject.getValue()); + + if (withinText.isNull() || startNumValue <= 0 || startNumValue > withinTextValue.length) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + if (findText.isNull() || findTextValue.length === 0) { + return NumberValueObject.create(startNumValue); + } + + const index = withinTextValue.indexOf(findTextValue, startNumValue - 1); + + if (index === -1) { + return ErrorValueObject.create(ErrorType.VALUE); + } + + const result = charLenByte(withinTextValue.substring(0, index)) + 1; + + return NumberValueObject.create(result); + } +} diff --git a/packages/engine-formula/src/functions/text/textjoin/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/textjoin/__test__/index.spec.ts new file mode 100644 index 00000000000..18ab2a0976f --- /dev/null +++ b/packages/engine-formula/src/functions/text/textjoin/__test__/index.spec.ts @@ -0,0 +1,170 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { ErrorValueObject } from '../../../../engine/value-object/base-value-object'; +import { BooleanValueObject, NullValueObject, NumberValueObject, StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Textjoin } from '../index'; + +describe('Test textjoin function', () => { + const testFunction = new Textjoin(FUNCTION_NAMES_TEXT.TEXTJOIN); + + describe('Textjoin', () => { + it('Value is normal', () => { + const delimiter = StringValueObject.create(', '); + const ignoreEmpty = BooleanValueObject.create(true); + const text1 = StringValueObject.create('Hi'); + const text2 = StringValueObject.create('Univer'); + const result = testFunction.calculate(delimiter, ignoreEmpty, text1, text2); + expect(getObjectValue(result)).toStrictEqual('Hi, Univer'); + }); + + it('Delimiter value test', () => { + const delimiter = NullValueObject.create(); + const ignoreEmpty = BooleanValueObject.create(true); + const text1 = StringValueObject.create('Hi'); + const text2 = StringValueObject.create('Univer'); + const result = testFunction.calculate(delimiter, ignoreEmpty, text1, text2); + expect(getObjectValue(result)).toStrictEqual('HiUniver'); + + const delimiter2 = BooleanValueObject.create(true); + const result2 = testFunction.calculate(delimiter2, ignoreEmpty, text1, text2); + expect(getObjectValue(result2)).toStrictEqual('HiTRUEUniver'); + + const delimiter3 = ErrorValueObject.create(ErrorType.NAME); + const result3 = testFunction.calculate(delimiter3, ignoreEmpty, text1, text2); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.NAME); + + const delimiter4 = ArrayValueObject.create('{"~","."}'); + const text3 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + ['Hi', 'Univer'], + [null, 'test'], + ]), + rowCount: 2, + columnCount: 2, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result4 = testFunction.calculate(delimiter4, ignoreEmpty, text3); + expect(getObjectValue(result4)).toStrictEqual('Hi~Univer.test'); + }); + + it('IgnoreEmpty value test', () => { + const delimiter = StringValueObject.create(', '); + const ignoreEmpty = NullValueObject.create(); + const text = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + ['Hi', 'Univer'], + [null, 'test'], + ]), + rowCount: 2, + columnCount: 2, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result = testFunction.calculate(delimiter, ignoreEmpty, text); + expect(getObjectValue(result)).toStrictEqual('Hi, Univer, , test'); + + const ignoreEmpty2 = NumberValueObject.create(2); + const result2 = testFunction.calculate(delimiter, ignoreEmpty2, text); + expect(getObjectValue(result2)).toStrictEqual('Hi, Univer, test'); + + const ignoreEmpty3 = StringValueObject.create('test'); + const result3 = testFunction.calculate(delimiter, ignoreEmpty3, text); + expect(getObjectValue(result3)).toStrictEqual(ErrorType.VALUE); + + const ignoreEmpty4 = ErrorValueObject.create(ErrorType.NAME); + const result4 = testFunction.calculate(delimiter, ignoreEmpty4, text); + expect(getObjectValue(result4)).toStrictEqual(ErrorType.NAME); + + const ignoreEmpty5 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [true, false], + ]), + rowCount: 1, + columnCount: 2, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result5 = testFunction.calculate(delimiter, ignoreEmpty5, text); + expect(getObjectValue(result5)).toStrictEqual([ + ['Hi, Univer, test', 'Hi, Univer, , test'], + ]); + + const ignoreEmpty6 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [3], + ]), + rowCount: 1, + columnCount: 1, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result6 = testFunction.calculate(delimiter, ignoreEmpty6, text); + expect(getObjectValue(result6)).toStrictEqual('Hi, Univer, test'); + }); + + it('Text value test', () => { + const delimiter = StringValueObject.create(', '); + const ignoreEmpty = BooleanValueObject.create(true); + const text = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + ['Hi', 'Univer', true], + [null, 'test', false], + ]), + rowCount: 2, + columnCount: 3, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result = testFunction.calculate(delimiter, ignoreEmpty, text); + expect(getObjectValue(result)).toStrictEqual('Hi, Univer, TRUE, test, FALSE'); + + const text2 = ErrorValueObject.create(ErrorType.NAME); + const result2 = testFunction.calculate(delimiter, ignoreEmpty, text, text2); + expect(getObjectValue(result2)).toStrictEqual(ErrorType.NAME); + }); + + it('More test', () => { + const delimiter = StringValueObject.create(''); + const ignoreEmpty = StringValueObject.create('true'); + const text1 = StringValueObject.create('1'); + const text2 = StringValueObject.create('2'); + const result = testFunction.calculate(delimiter, ignoreEmpty, text1, text2); + expect(getObjectValue(result)).toStrictEqual('12'); + + const ignoreEmpty2 = StringValueObject.create('false'); + const result2 = testFunction.calculate(delimiter, ignoreEmpty2, text1, text2); + expect(getObjectValue(result2)).toStrictEqual('12'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/textjoin/index.ts b/packages/engine-formula/src/functions/text/textjoin/index.ts new file mode 100644 index 00000000000..743583a76d4 --- /dev/null +++ b/packages/engine-formula/src/functions/text/textjoin/index.ts @@ -0,0 +1,164 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import type { BaseValueObject } from '../../../engine/value-object/base-value-object'; +import { checkVariantsErrorIsStringToNumber } from '../../../engine/utils/check-variant-error'; +import { ErrorValueObject } from '../../../engine/value-object/base-value-object'; +import { BooleanValueObject, StringValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Textjoin extends BaseFunction { + override minParams = 3; + + override maxParams = 255; + + override calculate(delimiter: BaseValueObject, ignoreEmpty: BaseValueObject, ...variants: BaseValueObject[]): BaseValueObject { + const delimiterValues = this._getDelimiterValues(delimiter); + const textValues = this._getTextValues(variants); + + if (ignoreEmpty.isArray()) { + const resultArray = ignoreEmpty.mapValue((ignoreEmptyObject) => this._handleSingleObject(delimiterValues, ignoreEmptyObject, textValues)); + + if ((resultArray as ArrayValueObject).getRowCount() === 1 && (resultArray as ArrayValueObject).getColumnCount() === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + const _ignoreEmpty = ignoreEmpty; + + if (_ignoreEmpty.isString()) { + const ignoreEmptyValue = `${_ignoreEmpty.getValue()}`.toLocaleUpperCase(); + + if (ignoreEmptyValue === 'TRUE') { + return this._handleSingleObject(delimiterValues, BooleanValueObject.create(true), textValues); + } + + if (ignoreEmptyValue === 'FALSE') { + return this._handleSingleObject(delimiterValues, BooleanValueObject.create(false), textValues); + } + } + + return this._handleSingleObject(delimiterValues, ignoreEmpty, textValues); + } + + private _handleSingleObject(delimiterValues: string[] | ErrorValueObject, ignoreEmpty: BaseValueObject, textValues: Array | ErrorValueObject): BaseValueObject { + if (delimiterValues instanceof ErrorValueObject) { + return delimiterValues as ErrorValueObject; + } + + const { isError, errorObject, variants } = checkVariantsErrorIsStringToNumber(ignoreEmpty); + + if (isError) { + return errorObject as ErrorValueObject; + } + + if (textValues instanceof ErrorValueObject) { + return textValues as ErrorValueObject; + } + + const [ignoreEmptyObject] = variants as BaseValueObject[]; + + const ignoreEmptyValue = +ignoreEmptyObject.getValue(); + + let _textValues = textValues; + + if (ignoreEmptyValue) { + _textValues = textValues.filter((value) => value !== null); + } + + let result = ''; + + for (let i = 0; i < _textValues.length; i++) { + if (_textValues[i] !== null) { + result += _textValues[i]; + } + + if (i < _textValues.length - 1) { + result += delimiterValues[i % delimiterValues.length]; + } + } + + return StringValueObject.create(result); + } + + private _getDelimiterValues(delimiter: BaseValueObject): string[] | ErrorValueObject { + const delimiterValues: string[] = []; + + const rowCount = delimiter.isArray() ? (delimiter as ArrayValueObject).getRowCount() : 1; + const columnCount = delimiter.isArray() ? (delimiter as ArrayValueObject).getColumnCount() : 1; + + for (let r = 0; r < rowCount; r++) { + for (let c = 0; c < columnCount; c++) { + const valueObject = delimiter.isArray() ? (delimiter as ArrayValueObject).get(r, c) as BaseValueObject : delimiter; + + if (valueObject.isError()) { + return valueObject as ErrorValueObject; + } + + let value = `${valueObject.getValue()}`; + + if (valueObject.isNull()) { + value = ''; + } + + if (valueObject.isBoolean()) { + value = value.toLocaleUpperCase(); + } + + delimiterValues.push(value); + } + } + + return delimiterValues; + } + + private _getTextValues(variants: BaseValueObject[]): Array | ErrorValueObject { + const textValues: Array = []; + + for (const variant of variants) { + const rowCount = variant.isArray() ? (variant as ArrayValueObject).getRowCount() : 1; + const columnCount = variant.isArray() ? (variant as ArrayValueObject).getColumnCount() : 1; + + for (let r = 0; r < rowCount; r++) { + for (let c = 0; c < columnCount; c++) { + const valueObject = variant.isArray() ? (variant as ArrayValueObject).get(r, c) as BaseValueObject : variant; + + if (valueObject.isError()) { + return valueObject as ErrorValueObject; + } + + if (valueObject.isNull()) { + textValues.push(null); + continue; + } + + let value = `${valueObject.getValue()}`; + + if (valueObject.isBoolean()) { + value = value.toLocaleUpperCase(); + } + + textValues.push(value); + } + } + } + + return textValues; + } +} diff --git a/packages/engine-formula/src/functions/text/trim/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/trim/__test__/index.spec.ts new file mode 100644 index 00000000000..6d3833ab786 --- /dev/null +++ b/packages/engine-formula/src/functions/text/trim/__test__/index.spec.ts @@ -0,0 +1,84 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Trim } from '../index'; + +describe('Test trim function', () => { + const testFunction = new Trim(FUNCTION_NAMES_TEXT.TRIM); + + describe('Trim', () => { + it('Value is normal', () => { + const text = StringValueObject.create(' Univer '); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual('Univer'); + }); + + it('Value is array', () => { + const text = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, '100', '2.34', '2-way street', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual([ + ['1', '', '中文测试', 'TRUE', 'FALSE', ''], + ['0', '100', '2.34', '2-way street', '-3', ErrorType.NAME], + ]); + + const text2 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [' Hello Univer '], + ]), + rowCount: 1, + columnCount: 1, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual('Hello Univer'); + }); + + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual(',。、;:{}'); + + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual('Hello中文o😊Wo😊rld'); + + const text3 = StringValueObject.create(' t est te st '); + const result3 = testFunction.calculate(text3); + expect(getObjectValue(result3)).toStrictEqual('t est te st'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/trim/index.ts b/packages/engine-formula/src/functions/text/trim/index.ts new file mode 100644 index 00000000000..aadaee27aa4 --- /dev/null +++ b/packages/engine-formula/src/functions/text/trim/index.ts @@ -0,0 +1,60 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import type { BaseValueObject } from '../../../engine/value-object/base-value-object'; +import { StringValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Trim extends BaseFunction { + override minParams = 1; + + override maxParams = 1; + + override calculate(text: BaseValueObject): BaseValueObject { + if (text.isArray()) { + const resultArray = text.mapValue((textObject) => this._handleSingleObject(textObject)); + + if ((resultArray as ArrayValueObject).getRowCount() === 1 && (resultArray as ArrayValueObject).getColumnCount() === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + return this._handleSingleObject(text); + } + + private _handleSingleObject(text: BaseValueObject): BaseValueObject { + if (text.isError()) { + return text; + } + + if (text.isNull()) { + return StringValueObject.create(''); + } + + let textValue = `${text.getValue()}`; + + if (text.isBoolean()) { + textValue = textValue.toLocaleUpperCase(); + } + + textValue = textValue.trim().replace(/\s+/g, ' '); + + return StringValueObject.create(textValue); + } +} diff --git a/packages/engine-formula/src/functions/text/upper/__test__/index.spec.ts b/packages/engine-formula/src/functions/text/upper/__test__/index.spec.ts new file mode 100644 index 00000000000..1645f198b18 --- /dev/null +++ b/packages/engine-formula/src/functions/text/upper/__test__/index.spec.ts @@ -0,0 +1,80 @@ +/** + * 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 { ErrorType } from '../../../../basics/error-type'; +import { ArrayValueObject, transformToValueObject } from '../../../../engine/value-object/array-value-object'; +import { StringValueObject } from '../../../../engine/value-object/primitive-object'; +import { getObjectValue } from '../../../__tests__/create-function-test-bed'; +import { FUNCTION_NAMES_TEXT } from '../../function-names'; +import { Upper } from '../index'; + +describe('Test upper function', () => { + const testFunction = new Upper(FUNCTION_NAMES_TEXT.UPPER); + + describe('Upper', () => { + it('Value is normal', () => { + const text = StringValueObject.create('univer'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual('UNIVER'); + }); + + it('Value is array', () => { + const text = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [1, ' ', '中文测试', true, false, null], + [0, '100', '2.34', '2-way street', -3, ErrorType.NAME], + ]), + rowCount: 2, + columnCount: 6, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual([ + ['1', ' ', '中文测试', 'TRUE', 'FALSE', ''], + ['0', '100', '2.34', '2-WAY STREET', '-3', ErrorType.NAME], + ]); + + const text2 = ArrayValueObject.create({ + calculateValueList: transformToValueObject([ + [' Hello Univer '], + ]), + rowCount: 1, + columnCount: 1, + unitId: '', + sheetId: '', + row: 0, + column: 0, + }); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual(' HELLO UNIVER '); + }); + + it('More test', () => { + const text = StringValueObject.create(',。、;:{}'); + const result = testFunction.calculate(text); + expect(getObjectValue(result)).toStrictEqual(',。、;:{}'); + + const text2 = StringValueObject.create('Hello中文o😊Wo😊rld'); + const result2 = testFunction.calculate(text2); + expect(getObjectValue(result2)).toStrictEqual('HELLO中文O😊WO😊RLD'); + }); + }); +}); diff --git a/packages/engine-formula/src/functions/text/upper/index.ts b/packages/engine-formula/src/functions/text/upper/index.ts new file mode 100644 index 00000000000..4780e089b53 --- /dev/null +++ b/packages/engine-formula/src/functions/text/upper/index.ts @@ -0,0 +1,54 @@ +/** + * 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 type { ArrayValueObject } from '../../../engine/value-object/array-value-object'; +import type { BaseValueObject } from '../../../engine/value-object/base-value-object'; +import { StringValueObject } from '../../../engine/value-object/primitive-object'; +import { BaseFunction } from '../../base-function'; + +export class Upper extends BaseFunction { + override minParams = 1; + + override maxParams = 1; + + override calculate(text: BaseValueObject): BaseValueObject { + if (text.isArray()) { + const resultArray = text.mapValue((textObject) => this._handleSingleObject(textObject)); + + if ((resultArray as ArrayValueObject).getRowCount() === 1 && (resultArray as ArrayValueObject).getColumnCount() === 1) { + return (resultArray as ArrayValueObject).get(0, 0) as BaseValueObject; + } + + return resultArray; + } + + return this._handleSingleObject(text); + } + + private _handleSingleObject(text: BaseValueObject): BaseValueObject { + if (text.isError()) { + return text; + } + + if (text.isNull()) { + return StringValueObject.create(''); + } + + const result = `${text.getValue()}`.toLocaleUpperCase(); + + return StringValueObject.create(result); + } +} diff --git a/packages/sheets-formula-ui/src/locale/function-list/text/en-US.ts b/packages/sheets-formula-ui/src/locale/function-list/text/en-US.ts index 74ee3a2a4a5..2f99c78c2b8 100644 --- a/packages/sheets-formula-ui/src/locale/function-list/text/en-US.ts +++ b/packages/sheets-formula-ui/src/locale/function-list/text/en-US.ts @@ -173,8 +173,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: 'find_text', detail: 'The text you want to find.' }, + withinText: { name: 'within_text', detail: 'The text containing the text you want to find.' }, + startNum: { name: 'start_num', detail: 'Specifies the character at which to start the search. If you omit start_num, it is assumed to be 1.' }, }, }, FINDB: { @@ -187,8 +188,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: 'find_text', detail: 'The text you want to find.' }, + withinText: { name: 'within_text', detail: 'The text containing the text you want to find.' }, + startNum: { name: 'start_num', detail: 'Specifies the character at which to start the search. If you omit start_num, it is assumed to be 1.' }, }, }, FIXED: { @@ -216,8 +218,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: 'text', detail: 'The text string containing the characters you want to extract.' }, + numChars: { name: 'num_chars', detail: 'Specifies the number of characters you want LEFT to extract.' }, }, }, LEFTB: { @@ -230,7 +232,7 @@ export default { }, ], functionParameter: { - text: { name: 'text', detail: 'The text string that contains the characters you want to extract.' }, + text: { name: 'text', detail: 'The text string containing the characters you want to extract.' }, numBytes: { name: 'num_bytes', detail: 'Specifies the number of characters you want LEFTB to extract, based on bytes.' }, }, }, @@ -270,10 +272,7 @@ export default { }, ], functionParameter: { - text: { - name: 'text', - detail: 'The text you want to convert to lowercase. LOWER does not change characters in text that are not letters.', - }, + text: { name: 'text', detail: 'The text you want to convert to lowercase.' }, }, }, MID: { @@ -287,8 +286,8 @@ export default { ], functionParameter: { text: { name: 'text', detail: 'The text string containing the characters you want to extract.' }, - startNum: { name: 'start_num', detail: 'The position of the first character you want to extract in text. The first character in text has start_num 1, and so on.\nIf start_num is greater than the length of text, MID/MIDB returns "" (empty text).\nIf start_num is less than the length of text, but start_num plus num_chars exceeds the length of text, MID/MIDB returns the characters up to the end of text.\nIf start_num is less than 1, MID/MIDB returns the #VALUE! error value.' }, - numChars: { name: 'num_chars', detail: 'Specifies the number of characters you want MID to return from text.\nIf num_chars is negative, MID returns the #VALUE! error value.' }, + startNum: { name: 'start_num', detail: 'The position of the first character you want to extract in text.' }, + numChars: { name: 'num_chars', detail: 'Specifies the number of characters you want MID to extract.' }, }, }, MIDB: { @@ -301,8 +300,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: 'text', detail: 'The text string containing the characters you want to extract.' }, + startNum: { name: 'start_num', detail: 'The position of the first character you want to extract in text.' }, + numBytes: { name: 'num_bytes', detail: 'Specifies the number of characters you want MIDB to extract, based on bytes.' }, }, }, NUMBERVALUE: { @@ -400,8 +400,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: 'old_text', detail: 'Text in which you want to replace some characters.' }, + startNum: { name: 'start_num', detail: 'The position of the character in old_text that you want to replace with new_text.' }, + numChars: { name: 'num_chars', detail: 'The number of characters in old_text that you want REPLACE to replace with new_text.' }, + newText: { name: 'new_text', detail: 'The text that will replace characters in old_text.' }, }, }, REPLACEB: { @@ -414,8 +416,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: 'old_text', detail: 'Text in which you want to replace some characters.' }, + startNum: { name: 'start_num', detail: 'The position of the character in old_text that you want to replace with new_text.' }, + numBytes: { name: 'num_bytes', detail: 'The number of bytes in old_text that you want REPLACEB to replace with new_text.' }, + newText: { name: 'new_text', detail: 'The text that will replace characters in old_text.' }, }, }, REPT: { @@ -470,8 +474,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: 'find_text', detail: 'The text you want to find.' }, + withinText: { name: 'within_text', detail: 'The text containing the text you want to find.' }, + startNum: { name: 'start_num', detail: 'Specifies the character at which to start the search. If you omit start_num, it is assumed to be 1.' }, }, }, SEARCHB: { @@ -484,8 +489,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: 'find_text', detail: 'The text you want to find.' }, + withinText: { name: 'within_text', detail: 'The text containing the text you want to find.' }, + startNum: { name: 'start_num', detail: 'Specifies the character at which to start the search. If you omit start_num, it is assumed to be 1.' }, }, }, SUBSTITUTE: { @@ -577,8 +583,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + delimiter: { name: 'delimiter', detail: 'A text string, either empty, or one or more characters enclosed by double quotes, or a reference to a valid text string.' }, + ignoreEmpty: { name: 'ignore_empty', detail: 'If TRUE, ignores empty cells.' }, + text1: { name: 'text1', detail: 'Text item to be joined. A text string, or array of strings, such as a range of cells.' }, + text2: { name: 'text2', detail: 'Additional text items to be joined. There can be a maximum of 252 text arguments for the text items, including text1. Each can be a text string, or array of strings, such as a range of cells.' }, }, }, TEXTSPLIT: { @@ -600,7 +608,7 @@ export default { }, }, TRIM: { - description: 'Removes spaces from text', + description: 'Removes all spaces from text except for single spaces between words.', abstract: 'Removes spaces from text', links: [ { @@ -609,8 +617,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: 'text', detail: 'The text from which you want spaces removed.' }, }, }, UNICHAR: { @@ -649,8 +656,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: 'text', detail: 'The text you want converted to uppercase.' }, }, }, VALUE: { diff --git a/packages/sheets-formula-ui/src/locale/function-list/text/ja-JP.ts b/packages/sheets-formula-ui/src/locale/function-list/text/ja-JP.ts index f2c830b60cc..2264f3e256d 100644 --- a/packages/sheets-formula-ui/src/locale/function-list/text/ja-JP.ts +++ b/packages/sheets-formula-ui/src/locale/function-list/text/ja-JP.ts @@ -174,8 +174,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '検索文字列', detail: '「検索するテキスト」で検索する文字列。' }, + withinText: { name: '検索するテキスト', detail: '「検索文字列」を検索する最初のテキスト。' }, + startNum: { name: '開始位置', detail: '「検索するテキスト」内の検索を開始する文字位置。省略した場合は、値 1 が想定されます。' }, }, }, FINDB: { @@ -188,8 +189,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '検索文字列', detail: '「検索するテキスト」で検索する文字列。' }, + withinText: { name: '検索するテキスト', detail: '「検索文字列」を検索する最初のテキスト。' }, + startNum: { name: '開始位置', detail: '「検索するテキスト」内の検索を開始する文字位置。省略した場合は、値 1 が想定されます。' }, }, }, FIXED: { @@ -217,8 +219,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字列', detail: '取り出す文字を含む文字列を指定します。' }, + numChars: { name: '文字数', detail: '取り出す文字数 (文字列の左端からの文字数) を指定します。' }, }, }, LEFTB: { @@ -232,7 +234,7 @@ export default { ], functionParameter: { text: { name: '文字列', detail: '取り出す文字を含む文字列を指定します。' }, - numBytes: { name: 'バイト数', detail: 'LEFTB で取り出す文字数をバイト数で指定します。' }, + numBytes: { name: 'バイト数', detail: '取り出す文字数をバイト数で指定します。' }, }, }, LEN: { @@ -271,10 +273,7 @@ export default { }, ], functionParameter: { - text: { - name: '文字列', - detail: '小文字に変換する文字列を指定します。 それ以外の文字は変換されません。', - }, + text: { name: '文字列', detail: '小文字に変換する文字列を指定します。' }, }, }, MID: { @@ -288,8 +287,8 @@ export default { ], functionParameter: { text: { name: '文字列', detail: '取り出す文字を含む文字列を指定します。' }, - startNum: { name: '開始位置', detail: '文字列から取り出す先頭文字の位置を数値で指定します。 文字列の先頭文字の位置が 1 になります。\nstart_numがテキストの長さより大きい場合、MID/MIDB は "" (空のテキスト) を返します。\nstart_numがテキストの長さより小さいが、start_numとnum_charsがテキストの長さを超える場合、MID/MIDB はテキストの末尾まで文字を返します。\nstart_numが 1 未満の場合、MID/MIDB は #VALUE を返します。 エラー値。' }, - numChars: { name: '文字数', detail: '取り出す文字数を指定します。\n文字数に負の数を指定すると、エラー値 #VALUE! が返されます。' }, + startNum: { name: '開始位置', detail: '文字列から取り出す先頭文字の位置を数値で指定します。' }, + numChars: { name: '文字数', detail: '取り出す文字数を指定します。' }, }, }, MIDB: { @@ -302,8 +301,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字列', detail: '取り出す文字を含む文字列を指定します。' }, + startNum: { name: '開始位置', detail: '文字列から取り出す先頭文字の位置を数値で指定します。' }, + numBytes: { name: 'バイト数', detail: '取り出す文字数をバイト数で指定します。' }, }, }, NUMBERVALUE: { @@ -397,12 +397,14 @@ export default { links: [ { title: '指導', - url: 'https://support.microsoft.com/ja-jp/office/replace-%E9%96%A2%E6%95%B0-replaceb-%E9%96%A2%E6%95%B0-8d799074-2425-4a8a-84bc-82472868878a', + url: 'https://support.microsoft.com/ja-jp/office/replace-replaceb-%E5%87%BD%E6%95%B0-8d799074-2425-4a8a-84bc-82472868878a', }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: '文字列', detail: '置き換えを行う文字列を指定します。' }, + startNum: { name: '開始位置', detail: '置換されるテキスト内の最初の文字の位置。' }, + numChars: { name: '文字数', detail: '置換す文字数を指定します。' }, + newText: { name: '置換文字列', detail: '文字列の一部と置き換える文字列を指定します。' }, }, }, REPLACEB: { @@ -411,12 +413,14 @@ export default { links: [ { title: '指導', - url: 'https://support.microsoft.com/ja-jp/office/replace-%E9%96%A2%E6%95%B0-replaceb-%E9%96%A2%E6%95%B0-8d799074-2425-4a8a-84bc-82472868878a', + url: 'https://support.microsoft.com/ja-jp/office/replace-replaceb-%E5%87%BD%E6%95%B0-8d799074-2425-4a8a-84bc-82472868878a', }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: '文字列', detail: '置き換えを行う文字列を指定します。' }, + startNum: { name: '開始位置', detail: '置換されるテキスト内の最初の文字の位置。' }, + numBytes: { name: 'バイト数', detail: '置換す文字数をバイト数で指定します。' }, + newText: { name: '置換文字列', detail: '文字列の一部と置き換える文字列を指定します。' }, }, }, REPT: { @@ -471,8 +475,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '検索文字列', detail: '「検索するテキスト」で検索する文字列。' }, + withinText: { name: '検索するテキスト', detail: '「検索文字列」を検索する最初のテキスト。' }, + startNum: { name: '開始位置', detail: '「検索するテキスト」内の検索を開始する文字位置。省略した場合は、値 1 が想定されます。' }, }, }, SEARCHB: { @@ -485,8 +490,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '検索文字列', detail: '「検索するテキスト」で検索する文字列。' }, + withinText: { name: '検索するテキスト', detail: '「検索文字列」を検索する最初のテキスト。' }, + startNum: { name: '開始位置', detail: '「検索するテキスト」内の検索を開始する文字位置。省略した場合は、値 1 が想定されます。' }, }, }, SUBSTITUTE: { @@ -578,8 +584,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + delimiter: { name: '区切り記号', detail: '空のテキスト文字列、または二重引用符で囲まれた 1 つ以上の文字、または有効なテキスト文字列への参照。' }, + ignoreEmpty: { name: '空のセルは無視', detail: 'TRUE の場合、空のセルは無視されます。' }, + text1: { name: '文字列1', detail: '結合するテキスト項目。 文字列またはセルの範囲などの文字列の配列。' }, + text2: { name: '文字列2', detail: '結合する追加のテキスト項目。 テキスト項目には、text1 を含め、最大 252 のテキスト引数を設定できます。 各引数には、文字列、またはセルの範囲などの文字列の配列を指定できます。' }, }, }, TEXTSPLIT: { @@ -601,7 +609,7 @@ export default { }, }, TRIM: { - description: '文字列から余分なスペースを削除します。', + description: '各単語間のスペースは 1 つ残し、不要なスペースをすべて削除します。', abstract: '文字列から余分なスペースを削除します。', links: [ { @@ -610,8 +618,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字列', detail: '余分なスペースを削除するテキストを指定します。' }, }, }, UNICHAR: { @@ -650,8 +657,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字列', detail: '大文字に変換する文字列を指定します。' }, }, }, VALUE: { diff --git a/packages/sheets-formula-ui/src/locale/function-list/text/ru-RU.ts b/packages/sheets-formula-ui/src/locale/function-list/text/ru-RU.ts index 5a4dd255153..f89ce3b6b04 100644 --- a/packages/sheets-formula-ui/src/locale/function-list/text/ru-RU.ts +++ b/packages/sheets-formula-ui/src/locale/function-list/text/ru-RU.ts @@ -25,8 +25,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст или ссылка на ячейку с текстом, который необходимо изменить. Если текст не содержит полноширинных знаков, он не изменяется.' }, }, }, ARRAYTOTEXT: { @@ -39,8 +38,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + array: { name: 'массив', detail: 'Массив, возвращаемый в виде текста.' }, + format: { name: 'формат', detail: 'Формат возвращаемых данных. Это может быть одно из двух значений:\n0 По умолчанию. Краткий формат, удобный для чтения.\n1 Строгий формат, включающий escape-символы и разделители строк. Создает строку, которую можно анализировать при вводе в строку формул. Заключает возвращаемые строки в кавычки, кроме логических значений, чисел и ошибок.' }, }, }, BAHTTEXT: { @@ -53,8 +52,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + number: { name: 'число', detail: 'Число, которое нужно преобразовать в текст, ссылка на ячейку, содержащую число, или формула, дающая в результате числовое значение.' }, }, }, CHAR: { @@ -67,8 +65,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + number: { name: 'число', detail: 'Число от 1 до 255, определяющее нужный знак. Знаки выбираются из набора знаков компьютера.' }, }, }, CLEAN: { @@ -81,8 +78,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Любые данные на листе, из которых нужно удалить непечатаемые знаки.' }, }, }, CODE: { @@ -95,8 +91,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст, для которого требуется узнать код первого знака.' }, }, }, CONCAT: { @@ -137,8 +132,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст или ссылка на ячейку с текстом, который необходимо изменить. Если текст не содержит полуширинных английских букв или знаков катаканы, он не изменяется.' }, }, }, DOLLAR: { @@ -151,8 +145,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + number: { name: 'число', detail: 'Число, ссылка на ячейку, содержащую число, или формула, вычисление которой дает число.' }, + decimals: { name: 'число_знаков', detail: 'Число цифр справа от десятичной запятой. Если значение отрицательное, число округляется слева от десятичной запятой. Если аргумент "число_знаков" опущен, то он полагается равным 2.' }, }, }, EXACT: { @@ -165,8 +159,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text1: { name: 'текст1', detail: 'Первая текстовая строка.' }, + text2: { name: 'текст2', detail: 'Вторая текстовая строка.' }, }, }, FIND: { @@ -179,8 +173,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + findText: { name: 'строка поиска', detail: 'Строка, которую нужно найти в «Текст для поиска».' }, + withinText: { name: 'текст для поиска', detail: 'Первое вхождение текста для поиска «строки поиска».' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция символа, с которой начинается поиск в «тексте для поиска». Если опущено, предполагается значение 1.' }, }, }, FINDB: { @@ -193,8 +188,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + findText: { name: 'строка поиска', detail: 'Строка, которую нужно найти в «Текст для поиска».' }, + withinText: { name: 'текст для поиска', detail: 'Первое вхождение текста для поиска «строки поиска».' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция символа, с которой начинается поиск в «тексте для поиска». Если опущено, предполагается значение 1.' }, }, }, FIXED: { @@ -207,8 +203,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + number: { name: 'число', detail: 'Число, которое требуется округлить и преобразовать в текст.' }, + decimals: { name: 'число_знаков', detail: 'Число цифр справа от десятичной запятой. Если значение отрицательное, число округляется слева от десятичной запятой. Если аргумент "число_знаков" опущен, то он полагается равным 2.' }, + noCommas: { name: 'без_разделителей', detail: 'Логическое значение, которое, если значение TRUE, не позволяет FIXED включать запятые в возвращаемый текст.' }, }, }, LEFT: { @@ -221,8 +218,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, + numChars: { name: 'количество_знаков', detail: 'Количество символов, извлекаемых функцией LEFT.' }, }, }, LEFTB: { @@ -235,8 +232,8 @@ export default { }, ], functionParameter: { - text: { name: 'Текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, - numBytes: { name: 'Количество_байт', detail: 'Количество символов, извлекаемых функцией ЛЕВБ.' }, + text: { name: 'текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, + numBytes: { name: 'количество_байтов', detail: 'Указывает в байтах количество символов, извлекаемых функцией LEFTB.' }, }, }, LEN: { @@ -275,10 +272,7 @@ export default { }, ], functionParameter: { - text: { - name: 'текст', - detail: 'Текст, который вы хотите преобразовать в нижний регистр. LOWER не изменяет символы в тексте, которые не являются буквами.', - }, + text: { name: 'tекст', detail: 'Текст, преобразуемый в нижний регистр.' }, }, }, MID: { @@ -291,9 +285,9 @@ export default { }, ], functionParameter: { - text: { name: 'Текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, - startNum: { name: 'Начальная_позиция', detail: 'Позиция первого знака, извлекаемого из текста. Первый знак в тексте имеет начальную позицию 1 и так далее.\nЕсли start_num больше длины текста, функция MID/MIDB возвращает значение "" (пустой текст).\nЕсли start_num меньше длины текста, но start_num плюс num_chars превышает длину текста, функция MID/MIDB возвращает символы до конца текста.\nЕсли start_num меньше 1, MID/MIDB возвращает #VALUE! (значение ошибки).' }, - numChars: { name: 'Количество_знаков', detail: 'Указывает, сколько знаков должна вернуть функция ПСТР.\nЕсли значение "число_знаков" отрицательно, функция ПСТР возвращает значение ошибки #ЗНАЧ!.' }, + text: { name: 'текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция первого знака, извлекаемого из текста.' }, + numChars: { name: 'количество_знаков', detail: 'Количество символов, извлекаемых функцией MID.' }, }, }, MIDB: { @@ -306,8 +300,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция первого знака, извлекаемого из текста.' }, + numBytes: { name: 'количество_байтов', detail: 'Указывает в байтах количество символов, извлекаемых функцией MIDB.' }, }, }, NUMBERVALUE: { @@ -320,8 +315,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст для преобразования в число.' }, + decimalSeparator: { name: 'десятичный_разделитель', detail: 'Символ, который будет использоваться для разделения дробной и целой части результата.' }, + groupSeparator: { name: 'разделитель_групп', detail: 'Символ, используемый для разделения групп цифр, например тысяч от сотен и миллионов от тысяч.' }, }, }, PHONETIC: { @@ -348,8 +344,50 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст в кавычках, формула, возвращающая текст, либо ссылка на ячейку, содержащую текст, в котором требуется заменить некоторые буквы на прописные.' }, + }, + }, + REGEXEXTRACT: { + description: 'Извлекает первые части текста, соответствующие регулярному выражению.', + abstract: 'Извлекает первые части текста, соответствующие регулярному выражению.', + links: [ + { + title: 'Инструкция', + url: 'https://support.google.com/docs/answer/3098244?sjid=5628197291201472796-AP&hl=ru-Ru', + }, + ], + functionParameter: { + text: { name: 'текст', detail: 'исходный текст.' }, + regularExpression: { name: 'регулярное_выражение', detail: 'заданное выражение. Будет показано первое совпадение с ним в тексте.' }, + }, + }, + REGEXMATCH: { + description: 'Проверяет, соответствует ли текст регулярному выражению.', + abstract: 'Проверяет, соответствует ли текст регулярному выражению.', + links: [ + { + title: 'Инструкция', + url: 'https://support.google.com/docs/answer/3098292?sjid=5628197291201472796-AP&hl=ru-Ru', + }, + ], + functionParameter: { + text: { name: 'текст', detail: 'текст, в котором проверяется наличие заданного выражения.' }, + regularExpression: { name: 'регулярное_выражение', detail: 'фраза, которую нужно найти в тексте.' }, + }, + }, + REGEXREPLACE: { + description: 'Заменяет часть строки на другой текст с помощью регулярного выражения.', + abstract: 'Заменяет часть строки на другой текст с помощью регулярного выражения.', + links: [ + { + title: '教学', + url: 'https://support.google.com/docs/answer/3098245?sjid=5628197291201472796-AP&hl=ru-Ru', + }, + ], + functionParameter: { + text: { name: 'текст', detail: 'текст, часть которого нужно заменить.' }, + regularExpression: { name: 'регулярное_выражение', detail: 'регулярное выражение. Все совпадения с ним в тексте будут заменены.' }, + replacement: { name: 'замена', detail: 'текст, который нужно вставить.' }, }, }, REPLACE: { @@ -362,8 +400,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + oldText: { name: 'старый текст', detail: 'Текст, в котором требуется заменить некоторые символы.' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция первого символа заменяемого текста.' }, + numChars: { name: 'количество_знаков', detail: 'Количество символов, замененных функцией REPLACE.' }, + newText: { name: 'текст замены', detail: 'Текст, который заменит символы в старом тексте.' }, }, }, REPLACEB: { @@ -376,8 +416,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + oldText: { name: 'старый текст', detail: 'Текст, в котором требуется заменить некоторые символы.' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция первого символа заменяемого текста.' }, + numBytes: { name: 'количество_байтов', detail: 'Указывает в байтах количество символов, которые заменяет функция REPLACEB.' }, + newText: { name: 'текст замены', detail: 'Текст, который заменит символы в старом тексте.' }, }, }, REPT: { @@ -390,8 +432,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Повторяемый текст.' }, + numberTimes: { name: 'число_повторений', detail: 'Положительное число, определяющее, сколько раз требуется повторить текст.' }, }, }, RIGHT: { @@ -404,8 +446,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, + numChars: { name: 'количество_знаков', detail: 'Количество символов, извлекаемых функцией RIGHT.' }, }, }, RIGHTB: { @@ -418,8 +460,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текстовая строка, содержащая символы, которые требуется извлечь.' }, + numBytes: { name: 'количество_байтов', detail: 'Указывает в байтах количество символов, извлекаемых функцией RIGHTB.' }, }, }, SEARCH: { @@ -432,8 +474,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + findText: { name: 'строка поиска', detail: 'Строка, которую нужно найти в «Текст для поиска».' }, + withinText: { name: 'текст для поиска', detail: 'Первое вхождение текста для поиска «строки поиска».' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция символа, с которой начинается поиск в «тексте для поиска». Если опущено, предполагается значение 1.' }, }, }, SEARCHB: { @@ -446,8 +489,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + findText: { name: 'строка поиска', detail: 'Строка, которую нужно найти в «Текст для поиска».' }, + withinText: { name: 'текст для поиска', detail: 'Первое вхождение текста для поиска «строки поиска».' }, + startNum: { name: 'стартовая позиция', detail: 'Позиция символа, с которой начинается поиск в «тексте для поиска». Если опущено, предполагается значение 1.' }, }, }, SUBSTITUTE: { @@ -460,8 +504,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст или ссылка на ячейку, содержащую текст, в котором подставляются знаки.' }, + oldText: { name: 'стар_текст', detail: 'Заменяемый текст.' }, + newText: { name: 'нов_текст', detail: 'Текст, на который заменяется "стар_текст".' }, + instanceNum: { name: 'номер_вхождения', detail: 'Определяет, какое вхождение фрагмента "стар_текст" нужно заменить фрагментом "нов_текст". Если этот аргумент определен, то заменяется только заданное вхождение фрагмента "стар_текст". В противном случае все вхождения фрагмента "стар_текст" в тексте заменяются фрагментом "нов_текст".' }, }, }, T: { @@ -474,8 +520,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + value: { name: 'значение', detail: 'Проверяемое значение.' }, }, }, TEXT: { @@ -502,8 +547,12 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст, в котором производится поиск. Использовать подстановочные знаки не разрешено.' }, + delimiter: { name: 'разделитель', detail: 'Текст, помечающий точку, после которой нужно извлечь текст.' }, + instanceNum: { name: 'номер_вхождения', detail: 'Экземпляр разделителя, после которого вы хотите извлечь текст.' }, + matchMode: { name: 'шаблон соответствия', detail: 'Определяет, учитывается ли регистр в текстовом поиске. По умолчанию регистр учитывается.' }, + matchEnd: { name: 'конец матча', detail: 'Рассматривает конец текста как разделитель. По умолчанию текст является точным совпадением.' }, + ifNotFound: { name: 'если не найдено', detail: 'Значение возвращается, если совпадение не найдено. По умолчанию возвращается значение #N/A.' }, }, }, TEXTBEFORE: { @@ -516,8 +565,12 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст, в котором производится поиск. Использовать подстановочные знаки не разрешено.' }, + delimiter: { name: 'разделитель', detail: 'Текст, помечающий точку, после которой нужно извлечь текст.' }, + instanceNum: { name: 'номер_вхождения', detail: 'Экземпляр разделителя, после которого вы хотите извлечь текст.' }, + matchMode: { name: 'шаблон соответствия', detail: 'Определяет, учитывается ли регистр в текстовом поиске. По умолчанию регистр учитывается.' }, + matchEnd: { name: 'конец матча', detail: 'Рассматривает конец текста как разделитель. По умолчанию текст является точным совпадением.' }, + ifNotFound: { name: 'если не найдено', detail: 'Значение возвращается, если совпадение не найдено. По умолчанию возвращается значение #N/A.' }, }, }, TEXTJOIN: { @@ -530,8 +583,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + delimiter: { name: 'разделитель', detail: 'Текстовая строка (пустая или с символами в двойных кавычках) или ссылка на действительную текстовую строку.' }, + ignoreEmpty: { name: 'игнорировать_пустые', detail: 'В случае значения ИСТИНА игнорирует пустые ячейки.' }, + text1: { name: 'текст1', detail: 'Элемент текста, который нужно присоединить. Текстовая строка или массив строк, например диапазон ячеек.' }, + text2: { name: 'текст2', detail: 'Дополнительные текстовые элементы для объединения. Для текстовых элементов можно указать до 252 аргументов, включая текст1. Каждый из них может быть текстовой строкой или массивом строк, например диапазоном ячеек.' }, }, }, TEXTSPLIT: { @@ -544,12 +599,16 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст, который нужно разделить.' }, + colDelimiter: { name: 'разделитель_столбцов', detail: 'Текст, помечающий точку, в которой текст разлит по столбцам.' }, + rowDelimiter: { name: 'разделитель_строк', detail: 'Текст, помечающий точку, в которую следует слить текст вниз по строкам.' }, + ignoreEmpty: { name: 'игнорировать_пустые', detail: 'В случае значения ИСТИНА игнорирует пустые ячейки.' }, + matchMode: { name: 'шаблон соответствия', detail: 'Определяет, учитывается ли регистр в текстовом поиске. По умолчанию регистр учитывается.' }, + padWith: { name: 'заполняющее_значение', detail: 'Значение, которым нужно дополнить результат. Значение по умолчанию: #N/A.' }, }, }, TRIM: { - description: 'Удаляет пробелы из текста', + description: 'Удаляет из текста все пробелы, за исключением одиночных пробелов между словами.', abstract: 'Удаляет пробелы из текста', links: [ { @@ -558,8 +617,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'tекст', detail: 'Текст, из которого удаляются пробелы.' }, }, }, UNICHAR: { @@ -572,8 +630,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + number: { name: 'число', detail: 'это число Юникод, которое представляет символ.' }, }, }, UNICODE: { @@ -586,8 +643,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'это символ, для которого необходимо установить значение Юникод.' }, }, }, UPPER: { @@ -600,8 +656,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'tекст', detail: 'Текст, преобразуемый в верхний регистр.' }, }, }, VALUE: { @@ -614,8 +669,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + text: { name: 'текст', detail: 'Текст в кавычках или ссылка на ячейку, содержащую текст, который нужно преобразовать.' }, }, }, VALUETOTEXT: { @@ -628,8 +682,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'первый' }, - number2: { name: 'number2', detail: 'второй' }, + value: { name: 'значение', detail: 'Значение, возвращаемое в виде текста.' }, + format: { name: 'формат', detail: 'Формат возвращаемых данных. Это может быть одно из двух значений:\n0 По умолчанию. Краткий формат, удобный для чтения.\n1 Строгий формат, включающий escape-символы и разделители строк. Создает строку, которую можно анализировать при вводе в строку формул. Заключает возвращаемые строки в кавычки, кроме логических значений, чисел и ошибок.' }, }, }, CALL: { diff --git a/packages/sheets-formula-ui/src/locale/function-list/text/vi-VN.ts b/packages/sheets-formula-ui/src/locale/function-list/text/vi-VN.ts index c8bf25ce4fc..4d81cad713b 100644 --- a/packages/sheets-formula-ui/src/locale/function-list/text/vi-VN.ts +++ b/packages/sheets-formula-ui/src/locale/function-list/text/vi-VN.ts @@ -164,18 +164,33 @@ export default { }, }, FIND: { - description: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (phân biệt chữ hoa, chữ thường)', - abstract: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (phân biệt chữ hoa, chữ thường)', + description: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (phân biệt chữ hoa chữ thường)', + abstract: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (phân biệt chữ hoa chữ thường)', links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/find-%E5%87%BD%E6%95%B0-dbcdf877-a93f-4d9e-a60d-b360804a3a5a', + url: 'https://support.microsoft.com/vi-vn/office/find-findb-%E5%87%BD%E6%95%B0-c7912941-af2a-4bdf-a553-d0d89b0a0628', }, ], functionParameter: { - find_text: { name: 'find_text', detail: 'Văn bản bạn muốn tìm.' }, - within_text: { name: 'within_text', detail: 'Văn bản mà bạn muốn tìm trong đó.' }, - start_num: { name: 'start_num', detail: 'Số ký tự mà bạn muốn bắt đầu tìm kiếm.' }, + findText: { name: 'chuỗi tìm kiếm', detail: 'Chuỗi cần tìm trong "Văn bản cần tìm kiếm".' }, + withinText: { name: 'văn bản để tìm kiếm', detail: 'Lần xuất hiện đầu tiên của văn bản để tìm kiếm "chuỗi tìm kiếm".' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Vị trí ký tự để bắt đầu tìm kiếm trong "văn bản cần tìm kiếm". Nếu bỏ qua, giá trị là 1 được giả định.' }, + }, + }, + FINDB: { + description: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (phân biệt chữ hoa chữ thường)', + abstract: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (phân biệt chữ hoa chữ thường)', + links: [ + { + title: 'Hướng dẫn', + url: 'https://support.microsoft.com/vi-vn/office/find-findb-%E5%87%BD%E6%95%B0-c7912941-af2a-4bdf-a553-d0d89b0a0628', + }, + ], + functionParameter: { + findText: { name: 'chuỗi tìm kiếm', detail: 'Chuỗi cần tìm trong "Văn bản cần tìm kiếm".' }, + withinText: { name: 'văn bản để tìm kiếm', detail: 'Lần xuất hiện đầu tiên của văn bản để tìm kiếm "chuỗi tìm kiếm".' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Vị trí ký tự để bắt đầu tìm kiếm trong "văn bản cần tìm kiếm". Nếu bỏ qua, giá trị là 1 được giả định.' }, }, }, FIXED: { @@ -194,17 +209,31 @@ export default { }, }, LEFT: { - description: 'Trả về một số ký tự cụ thể từ đầu của chuỗi văn bản', - abstract: 'Trả về một số ký tự cụ thể từ đầu của chuỗi văn bản', + description: 'Trả về ký tự ngoài cùng bên trái trong giá trị văn bản', + abstract: 'Trả về ký tự ngoài cùng bên trái trong giá trị văn bản', links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/left-leftb-%E5%87%BD%E6%95%B0-f64f8495-4e12-49ab-803c-91e37d7e70c2', + url: 'https://support.microsoft.com/vi-vn/office/left-leftb-%E5%87%BD%E6%95%B0-9203d2d2-7960-479b-84c6-1ea52b99640c', }, ], functionParameter: { - text: { name: 'text', detail: 'Chuỗi văn bản có chứa các ký tự bạn muốn trích xuất.' }, - num_chars: { name: 'num_chars', detail: 'Số lượng ký tự mà bạn muốn trích xuất.' }, + text: { name: 'bản văn', detail: 'Chuỗi văn bản chứa các ký tự bạn muốn trích xuất.' }, + numChars: { name: 'số ký tự', detail: 'Chỉ định số ký tự bạn muốn LEFT trích xuất.' }, + }, + }, + LEFTB: { + description: 'Trả về ký tự ngoài cùng bên trái trong giá trị văn bản', + abstract: 'Trả về ký tự ngoài cùng bên trái trong giá trị văn bản', + links: [ + { + title: 'Hướng dẫn', + url: 'https://support.microsoft.com/vi-vn/office/left-leftb-%E5%87%BD%E6%95%B0-9203d2d2-7960-479b-84c6-1ea52b99640c', + }, + ], + functionParameter: { + text: { name: 'bản văn', detail: 'Chuỗi văn bản chứa các ký tự bạn muốn trích xuất.' }, + numBytes: { name: 'số Byte', detail: 'Chỉ rõ số ký tự mà bạn muốn hàm LEFTB trích xuất, dựa trên byte.' }, }, }, LEN: { @@ -239,26 +268,41 @@ export default { links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/lower-%E5%87%BD%E6%95%B0-0b22ff44-f335-402b-b171-8f62a7a6d159', + url: 'https://support.microsoft.com/vi-vn/office/lower-%E5%87%BD%E6%95%B0-3f21df02-a80c-44b2-afaf-81358f9fdeb4', }, ], functionParameter: { - text: { name: 'bản văn', detail: 'Văn bản mà bạn muốn chuyển đổi thành chữ thường.' }, + text: { name: 'bản văn', detail: 'Văn bản mà bạn muốn chuyển đổi thành chữ thường.' }, }, }, MID: { - description: 'Trả về một số ký tự cụ thể từ một chuỗi văn bản bắt đầu tại vị trí mà bạn chỉ định', - abstract: 'Trả về một số ký tự cụ thể từ một chuỗi văn bản bắt đầu tại vị trí mà bạn chỉ định', + description: 'Trả về một số ký tự cụ thể bắt đầu tại một vị trí được chỉ định trong chuỗi văn bản', + abstract: 'Trả về một số ký tự cụ thể bắt đầu tại một vị trí được chỉ định trong chuỗi văn bản', links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/mid-midb-ha%CC%80m-mid-midb-d5f9e25c-d7d6-472e-b568-4ecb12433028', + url: 'https://support.microsoft.com/vi-vn/office/mid-midb-%E5%87%BD%E6%95%B0-d5f9e25c-d7d6-472e-b568-4ecb12433028', }, ], functionParameter: { - text: { name: 'text', detail: 'Chuỗi văn bản có chứa các ký tự mà bạn muốn trích xuất.' }, - start_num: { name: 'start_num', detail: 'Ví trí của ký tự thứ nhất mà bạn muốn trích xuất trong văn bản. Ký tự thứ nhất trong chuỗi văn bản có số bắt đầu là 1, và v.v.\nNếu start_num văn bản lớn hơn, thì hàm MID/MIDB trả về "" (văn bản trống).\nNếu start_num độ dài văn bản nhỏ hơn nhưng start_num cộng num_chars vượt quá độ dài văn bản, thì hàm MID/MIDB trả về các ký tự đến cuối văn bản.\nNếu start_num nhỏ hơn 1, thì hàm MID/MIDB trả về giá #VALUE! .' }, - num_chars: { name: 'num_chars', detail: 'Bắt buộc đối với hàm MID. Chỉ rõ số ký tự mà bạn muốn hàm MID trả về từ văn bản.\nNếu số ký tự là số âm, thì hàm MID trả về giá trị lỗi #VALUE! .' }, + text: { name: 'bản văn', detail: 'Chuỗi văn bản chứa các ký tự bạn muốn trích xuất.' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Ví trí của ký tự thứ nhất mà bạn muốn trích xuất trong văn bản.' }, + numChars: { name: 'số ký tự', detail: 'Chỉ định số ký tự bạn muốn MID trích xuất.' }, + }, + }, + MIDB: { + description: 'Trả về một số ký tự cụ thể bắt đầu tại một vị trí được chỉ định trong chuỗi văn bản', + abstract: 'Trả về một số ký tự cụ thể bắt đầu tại một vị trí được chỉ định trong chuỗi văn bản', + links: [ + { + title: 'Hướng dẫn', + url: 'https://support.microsoft.com/vi-vn/office/mid-midb-%E5%87%BD%E6%95%B0-d5f9e25c-d7d6-472e-b568-4ecb12433028', + }, + ], + functionParameter: { + text: { name: 'bản văn', detail: 'Chuỗi văn bản chứa các ký tự bạn muốn trích xuất.' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Ví trí của ký tự thứ nhất mà bạn muốn trích xuất trong văn bản.' }, + numBytes: { name: 'số Byte', detail: 'Chỉ rõ số ký tự mà bạn muốn hàm MIDB trích xuất, dựa trên byte.' }, }, }, NUMBERVALUE: { @@ -347,19 +391,35 @@ export default { }, }, REPLACE: { - description: 'Thay thế một phần của chuỗi văn bản bằng một chuỗi văn bản khác', - abstract: 'Thay thế một phần của chuỗi văn bản bằng một chuỗi văn bản khác', + description: 'Thay thế ký tự trong văn bản', + abstract: 'Thay thế ký tự trong văn bản', + links: [ + { + title: 'Hướng dẫn', + url: 'https://support.microsoft.com/vi-vn/office/replace-replaceb-%E5%87%BD%E6%95%B0-8d799074-2425-4a8a-84bc-82472868878a', + }, + ], + functionParameter: { + oldText: { name: 'văn bản cũ', detail: 'Văn bản mà bạn muốn thay thế một vài ký tự trong đó.' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Vị trí của ký tự đầu tiên trong văn bản cần thay thế.' }, + numChars: { name: 'số ký tự', detail: 'Chỉ định số ký tự bạn muốn REPLACE thay thế.' }, + newText: { name: 'văn bản thay thế', detail: 'Văn bản sẽ thay thế các ký tự trong văn bản cũ.' }, + }, + }, + REPLACEB: { + description: 'Thay thế ký tự trong văn bản', + abstract: 'Thay thế ký tự trong văn bản', links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/replace-replaceb-%E5%87%BD%E6%95%B0-86e5e09d-1401-41a8-890b-8e692f1f46e5', + url: 'https://support.microsoft.com/vi-vn/office/replace-replaceb-%E5%87%BD%E6%95%B0-8d799074-2425-4a8a-84bc-82472868878a', }, ], functionParameter: { - old_text: { name: 'old_text', detail: 'Văn bản bạn muốn thay thế một phần của nó.' }, - start_num: { name: 'start_num', detail: 'Vị trí của ký tự đầu tiên bạn muốn thay thế trong văn bản.' }, - num_chars: { name: 'num_chars', detail: 'Số lượng ký tự bạn muốn thay thế.' }, - new_text: { name: 'new_text', detail: 'Văn bản thay thế.' }, + oldText: { name: 'văn bản cũ', detail: 'Văn bản mà bạn muốn thay thế một vài ký tự trong đó.' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Vị trí của ký tự đầu tiên trong văn bản cần thay thế.' }, + numBytes: { name: 'số Byte', detail: 'Chỉ định, tính bằng byte, số lượng ký tự được thay thế bằng REPLACEB.' }, + newText: { name: 'văn bản thay thế', detail: 'Văn bản sẽ thay thế các ký tự trong văn bản cũ.' }, }, }, REPT: { @@ -405,8 +465,23 @@ export default { }, }, SEARCH: { - description: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (không phân biệt chữ hoa, chữ thường)', - abstract: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (không phân biệt chữ hoa, chữ thường)', + description: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (không phân biệt chữ hoa chữ thường)', + abstract: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (không phân biệt chữ hoa chữ thường)', + links: [ + { + title: 'Hướng dẫn', + url: 'https://support.microsoft.com/vi-vn/office/search-searchb-%E5%87%BD%E6%95%B0-dfb12d6f-c60d-4a40-b090-7d2617b49e11', + }, + ], + functionParameter: { + findText: { name: 'chuỗi tìm kiếm', detail: 'Chuỗi cần tìm trong "Văn bản cần tìm kiếm".' }, + withinText: { name: 'văn bản để tìm kiếm', detail: 'Lần xuất hiện đầu tiên của văn bản để tìm kiếm "chuỗi tìm kiếm".' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Vị trí ký tự để bắt đầu tìm kiếm trong "văn bản cần tìm kiếm". Nếu bỏ qua, giá trị là 1 được giả định.' }, + }, + }, + SEARCHB: { + description: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (không phân biệt chữ hoa chữ thường)', + abstract: 'Trả về vị trí của một chuỗi văn bản trong một chuỗi văn bản khác (không phân biệt chữ hoa chữ thường)', links: [ { title: 'Hướng dẫn', @@ -414,9 +489,9 @@ export default { }, ], functionParameter: { - find_text: { name: 'find_text', detail: 'Văn bản bạn muốn tìm.' }, - within_text: { name: 'within_text', detail: 'Văn bản mà bạn muốn tìm trong đó.' }, - start_num: { name: 'start_num', detail: 'Số ký tự mà bạn muốn bắt đầu tìm kiếm.' }, + findText: { name: 'chuỗi tìm kiếm', detail: 'Chuỗi cần tìm trong "Văn bản cần tìm kiếm".' }, + withinText: { name: 'văn bản để tìm kiếm', detail: 'Lần xuất hiện đầu tiên của văn bản để tìm kiếm "chuỗi tìm kiếm".' }, + startNum: { name: 'vị trí bắt đầu', detail: 'Vị trí ký tự để bắt đầu tìm kiếm trong "văn bản cần tìm kiếm". Nếu bỏ qua, giá trị là 1 được giả định.' }, }, }, SPLIT: { @@ -520,14 +595,14 @@ export default { links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/textjoin-%E5%87%BD%E6%95%B0-c50e06da-9c72-4cae-a5a3-1e6d42bd43e1', + url: 'https://support.microsoft.com/vi-vn/office/textjoin-%E5%87%BD%E6%95%B0-357b449a-ec91-49d0-80c3-0e8fc845691c', }, ], functionParameter: { - delimiter: { name: 'delimiter', detail: 'Dấu phân cách để sử dụng giữa các văn bản.' }, - ignore_empty: { name: 'ignore_empty', detail: 'Giá trị logic để chỉ định liệu bỏ qua các ô trống.' }, - text1: { name: 'text1', detail: 'Chuỗi văn bản đầu tiên để kết hợp.' }, - text2: { name: 'text2', detail: 'Chuỗi văn bản tiếp theo để kết hợp.' }, + delimiter: { name: 'dấu tách', detail: 'Một chuỗi văn bản, trống hoặc có một hay nhiều ký tự nằm giữa các dấu ngoặc kép hay một tham chiếu tới một chuỗi văn bản hợp lệ.' }, + ignoreEmpty: { name: 'bỏ qua các ô trống', detail: 'Nếu TRUE, hãy bỏ qua các ô trống.' }, + text1: { name: 'bản văn 1', detail: 'Mục văn bản cần kết hợp. Một chuỗi văn bản hoặc xâu chuỗi, chẳng hạn như một phạm vi ô.' }, + text2: { name: 'bản văn 2', detail: 'Các mục văn bản bổ sung cần kết hợp. Có thể có tối đa 252 tham đối văn bản cho các mục văn bản, bao gồm text1. Mỗi tham đối có thể là một chuỗi văn bản hoặc xâu chuỗi, chẳng hạn như phạm vi ô.' }, }, }, TEXTSPLIT: { @@ -549,16 +624,16 @@ export default { }, }, TRIM: { - description: 'Loại bỏ tất cả các khoảng trắng khỏi chuỗi văn bản ngoại trừ các khoảng trắng đơn giữa các từ', - abstract: 'Loại bỏ tất cả các khoảng trắng khỏi chuỗi văn bản ngoại trừ các khoảng trắng đơn giữa các từ', + description: 'Loại bỏ tất cả khoảng trống ra khỏi văn bản, chỉ để lại một khoảng trống giữa các từ.', + abstract: 'Xóa khoảng trắng khỏi văn bản', links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/trim-%E5%87%BD%E6%95%B0-410388fa-c5df-49c6-b16c-9e5630b479d0', + url: 'https://support.microsoft.com/vi-vn/office/trim-%E5%87%BD%E6%95%B0-410388fa-c5df-49c6-b16c-9e5630b479f9', }, ], functionParameter: { - text: { name: 'text', detail: 'Chuỗi văn bản mà bạn muốn loại bỏ các khoảng trắng.' }, + text: { name: 'bản văn', detail: 'Văn bản bạn muốn loại bỏ các khoảng trống.' }, }, }, UNICHAR: { @@ -593,11 +668,11 @@ export default { links: [ { title: 'Hướng dẫn', - url: 'https://support.microsoft.com/vi-vn/office/upper-%E5%87%BD%E6%95%B0-8a57ae40-4fa5-4754-a65b-9b6745c4a5f0', + url: 'https://support.microsoft.com/vi-vn/office/upper-%E5%87%BD%E6%95%B0-c11f29b3-d1a3-4537-8df6-04d0049963d6', }, ], functionParameter: { - text: { name: 'text', detail: 'Văn bản mà bạn muốn chuyển đổi thành chữ hoa.' }, + text: { name: 'bản văn', detail: 'Văn bản bạn muốn chuyển đổi thành chữ hoa.' }, }, }, VALUE: { diff --git a/packages/sheets-formula-ui/src/locale/function-list/text/zh-CN.ts b/packages/sheets-formula-ui/src/locale/function-list/text/zh-CN.ts index 8b29e4dcfdc..7c01c8473a5 100644 --- a/packages/sheets-formula-ui/src/locale/function-list/text/zh-CN.ts +++ b/packages/sheets-formula-ui/src/locale/function-list/text/zh-CN.ts @@ -173,8 +173,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜索字符串', detail: '要在“要搜索的文本”中查找的字符串。' }, + withinText: { name: '要搜索的文本', detail: '要搜索“搜索字符串”的首次出现的文本。' }, + startNum: { name: '开始位置', detail: '要在“要搜索的文本”中开始搜索的字符位置。如果省略则假定其值为 1。' }, }, }, FINDB: { @@ -187,8 +188,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜索字符串', detail: '要在“要搜索的文本”中查找的字符串。' }, + withinText: { name: '要搜索的文本', detail: '要搜索“搜索字符串”的首次出现的文本。' }, + startNum: { name: '开始位置', detail: '要在“要搜索的文本”中开始搜索的字符位置。如果省略则假定其值为 1。' }, }, }, FIXED: { @@ -216,8 +218,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文本', detail: '包含要提取字符的文本字符串。' }, + numChars: { name: '字符数', detail: '指定希望 LEFT 提取的字符数。' }, }, }, LEFTB: { @@ -230,7 +232,7 @@ export default { }, ], functionParameter: { - text: { name: '文本', detail: '包含要提取的字符的文本字符串。' }, + text: { name: '文本', detail: '包含要提取字符的文本字符串。' }, numBytes: { name: '字节数', detail: '按字节指定要由 LEFTB 提取的字符的数量。' }, }, }, @@ -270,10 +272,7 @@ export default { }, ], functionParameter: { - text: { - name: '文本', - detail: '要转换为小写字母的文本。 LOWER 不改变文本中的非字母字符。', - }, + text: { name: '文本', detail: '要转换为小写字母的文本。' }, }, }, MID: { @@ -287,8 +286,8 @@ export default { ], functionParameter: { text: { name: '文本', detail: '包含要提取字符的文本字符串。' }, - startNum: { name: '开始位置', detail: '文本中要提取的第一个字符的位置。 文本中第一个字符的 start_num 为 1,以此类推。\n如果start_num大于文本长度,则 MID/MIDB 将返回“” (空文本) 。\n如果start_num小于文本长度,但start_num加num_chars超过文本长度,则 MID/MIDB 将返回字符到文本末尾。\n如果start_num小于 1,则 MID/MIDB 返回 #VALUE! 。' }, - numChars: { name: '字符数', detail: '指定希望 MID 从文本中返回字符的个数。\n如果num_chars为负数,则 MID 返回 #VALUE! 。' }, + startNum: { name: '开始位置', detail: '文本中要提取的第一个字符的位置。' }, + numChars: { name: '字符数', detail: '指定希望 MID 提取的字符数。' }, }, }, MIDB: { @@ -301,8 +300,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文本', detail: '包含要提取字符的文本字符串。' }, + startNum: { name: '开始位置', detail: '文本中要提取的第一个字符的位置。' }, + numBytes: { name: '字节数', detail: '按字节指定要由 MIDB 提取的字符的数量。' }, }, }, NUMBERVALUE: { @@ -400,8 +400,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: '旧文本', detail: '要替换其部分字符的文本。' }, + startNum: { name: '开始位置', detail: '文本中要替换的第一个字符的位置。' }, + numChars: { name: '字符数', detail: '指定希望 REPLACE 替换的字符数。' }, + newText: { name: '替换文本', detail: '将替换旧文本中字符的文本。' }, }, }, REPLACEB: { @@ -414,8 +416,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: '旧文本', detail: '要替换其部分字符的文本。' }, + startNum: { name: '开始位置', detail: '文本中要替换的第一个字符的位置。' }, + numBytes: { name: '字节数', detail: '按字节指定要由 REPLACEB 替换的字符的数量。' }, + newText: { name: '替换文本', detail: '将替换旧文本中字符的文本。' }, }, }, REPT: { @@ -470,8 +474,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜索字符串', detail: '要在“要搜索的文本”中查找的字符串。' }, + withinText: { name: '要搜索的文本', detail: '要搜索“搜索字符串”的首次出现的文本。' }, + startNum: { name: '开始位置', detail: '要在“要搜索的文本”中开始搜索的字符位置。如果省略则假定其值为 1。' }, }, }, SEARCHB: { @@ -484,8 +489,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜索字符串', detail: '要在“要搜索的文本”中查找的字符串。' }, + withinText: { name: '要搜索的文本', detail: '要搜索“搜索字符串”的首次出现的文本。' }, + startNum: { name: '开始位置', detail: '要在“要搜索的文本”中开始搜索的字符位置。如果省略则假定其值为 1。' }, }, }, SUBSTITUTE: { @@ -577,8 +583,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + delimiter: { name: '分隔符', detail: '文本字符串,或者为空,或用双引号引起来的一个或多个字符,或对有效文本字符串的引用。' }, + ignoreEmpty: { name: '忽略空白', detail: '如果为 TRUE,则忽略空白单元格。' }, + text1: { name: '文本1', detail: '要联接的文本项。 文本字符串或字符串数组,如单元格区域中。' }, + text2: { name: '文本2', detail: '要联接的其他文本项。 文本项最多可以包含 252 个文本参数 text1。 每个参数可以是一个文本字符串或字符串数组,如单元格区域。' }, }, }, TEXTSPLIT: { @@ -600,7 +608,7 @@ export default { }, }, TRIM: { - description: '删除文本中的空格', + description: '除了单词之间的单个空格之外,删除文本中的所有空格。', abstract: '删除文本中的空格', links: [ { @@ -609,8 +617,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文本', detail: '要从中删除空格的文本。' }, }, }, UNICHAR: { @@ -649,8 +656,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文本', detail: '要转换为大写字母的文本。' }, }, }, VALUE: { diff --git a/packages/sheets-formula-ui/src/locale/function-list/text/zh-TW.ts b/packages/sheets-formula-ui/src/locale/function-list/text/zh-TW.ts index 9c3130e9a38..2395bccebf5 100644 --- a/packages/sheets-formula-ui/src/locale/function-list/text/zh-TW.ts +++ b/packages/sheets-formula-ui/src/locale/function-list/text/zh-TW.ts @@ -25,7 +25,7 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是文字或儲存格參照,其中包含所要變更的文字。 如果文字中不包含任何全形字母,則文字不會變更。' }, + text: { name: '文字', detail: '文字或儲存格參照,其中包含所要變更的文字。 如果文字中不包含任何全形字母,則文字不會變更。' }, }, }, ARRAYTOTEXT: { @@ -65,7 +65,7 @@ export default { }, ], functionParameter: { - number: { name: '數值', detail: '這是介於 1 和 255 之間的數字,用以指定您所需的字元。 此字元來自您電腦所使用的字元集。' }, + number: { name: '數值', detail: '介於 1 和 255 之間的數字,用以指定所需的字元。' }, }, }, CLEAN: { @@ -78,7 +78,7 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是要從中移除無法列印之字元的任何工作表資訊。' }, + text: { name: '文字', detail: '要從中移除無法列印之字元的任何工作表資訊。' }, }, }, CODE: { @@ -91,7 +91,7 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是欲求其第一個字元代碼的文字。' }, + text: { name: '文字', detail: '欲求其第一個字元代碼的文字。' }, }, }, CONCAT: { @@ -132,7 +132,7 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是文字或儲存格參照,其中包含所要變更的文字。 如果文字中不包含任何半形英文字母或片假名,文字就不會變更。' }, + text: { name: '文字', detail: '文字或儲存格參照,其中包含所要變更的文字。 如果文字中不包含任何半形英文字母或片假名,文字就不會變更。' }, }, }, DOLLAR: { @@ -145,8 +145,8 @@ export default { }, ], functionParameter: { - number: { name: '數值', detail: '這是一個數字、一個含有數字之儲存格的參照,或一個評估為數字的公式。' }, - decimals: { name: '小數位數', detail: '這是小數點右邊的小數位數。 如果這是負數,則會將數位四捨五入到小數點的左邊。 如果您省略 decimals,則假設為 2。' }, + number: { name: '數值', detail: '一個數字、一個含有數字之儲存格的參照,或一個評估為數字的公式。' }, + decimals: { name: '小數位數', detail: '小數點右邊的小數位數。 如果是負數,則會將數位四捨五入到小數點的左邊。 如果省略 decimals,則假設為 2。' }, }, }, EXACT: { @@ -159,8 +159,8 @@ export default { }, ], functionParameter: { - text1: { name: '文字1', detail: '這是第一個文字字串。' }, - text2: { name: '文字2', detail: '這是第二個文字字串。' }, + text1: { name: '文字1', detail: '第一個文字字串。' }, + text2: { name: '文字2', detail: '第二個文字字串。' }, }, }, FIND: { @@ -173,8 +173,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜尋字串', detail: '要在“要搜尋的文字”中尋找的字串。' }, + withinText: { name: '要搜尋的文字', detail: '要搜尋“搜尋字串”的首次出現的文字。' }, + startNum: { name: '開始位置', detail: '要在“要搜尋的文字”中開始搜尋的字元位置。若省略則假定其值為 1。' }, }, }, FINDB: { @@ -187,8 +188,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜尋字串', detail: '要在“要搜尋的文字”中尋找的字串。' }, + withinText: { name: '要搜尋的文字', detail: '要搜尋“搜尋字串”的首次出現的文字。' }, + startNum: { name: '開始位置', detail: '要在“要搜尋的文字”中開始搜尋的字元位置。若省略則假定其值為 1。' }, }, }, FIXED: { @@ -201,9 +203,9 @@ export default { }, ], functionParameter: { - number: { name: '數值', detail: '這是要四捨五入並轉換為文字的數字。' }, - decimals: { name: '小數位數', detail: '這是小數點右邊的小數位數。 如果這是負數,則會將數位四捨五入到小數點的左邊。 如果您省略 decimals,則假設為 2。' }, - noCommas: { name: '禁用分隔符', detail: '這是邏輯值,如果為 TRUE,會阻止 FIXED 在傳回的文字中包含逗號。' }, + number: { name: '數值', detail: '要四捨五入並轉換為文字的數字。' }, + decimals: { name: '小數位數', detail: '小數點右邊的小數位數。 如果是負數,則會將數位四捨五入到小數點的左邊。 如果省略 decimals,則假設為 2。' }, + noCommas: { name: '禁用分隔符', detail: '邏輯值,如果為 TRUE,會阻止 FIXED 在傳回的文字中包含逗號。' }, }, }, LEFT: { @@ -216,8 +218,8 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字', detail: '包含想擷取之字元的文字字串。' }, + numChars: { name: '字元數', detail: '指定要 LEFT 擷取的字元數目。' }, }, }, LEFTB: { @@ -230,8 +232,8 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '內含所要擷取字元的文字字串。' }, - numBytes: { name: '字元數', detail: '指定 LEFTB 所要擷取的字元數,以位元組為單位。' }, + text: { name: '文字', detail: '包含想擷取之字元的文字字串。' }, + numBytes: { name: '字節數', detail: '指定要 LEFTB 擷取的字元數目,以位元組為單位。' }, }, }, LEN: { @@ -270,10 +272,7 @@ export default { }, ], functionParameter: { - text: { - name: '文字', - detail: '要轉換為小寫字母的文字。 LOWER 不會改變文字中的非字母字元。 ', - }, + text: { name: '文字', detail: '要轉換成小寫的文字。' }, }, }, MID: { @@ -286,9 +285,9 @@ export default { }, ], functionParameter: { - text: { name: 'text', detail: '包含想擷取之字元的文字字串。' }, - startNum: { name: 'start_num', detail: ' 要在文字中擷取之第一個字元的位置。 文字中的第一個字元start_num 1,依此類文字。\n如果start_num大於文字的長度,MID/MIDB 會傳回 “” (空白文字) 。\n如果start_num小於文字的長度,但start_num加上num_chars超過文字的長度,MID/MIDB 會傳回文字結尾的字元。\n如果 start_num 小於 1,MID/MIDB 會傳回 #VALUE! 錯誤值。' }, - numChars: { name: 'num_chars', detail: '指定要 MID 從文字傳回的字元數。\n如果 num_chars 為負數,MID 會傳回 #VALUE! 的錯誤值。' }, + text: { name: '文字', detail: '包含想擷取之字元的文字字串。' }, + startNum: { name: '開始位置', detail: '要在文字中擷取之第一個字元的位置。' }, + numChars: { name: '字元數', detail: '指定要 MID 擷取的字元數目。' }, }, }, MIDB: { @@ -300,8 +299,9 @@ export default { url: 'https://support.microsoft.com/zh-tw/office/mid-midb-%E5%87%BD%E6%95%B0-d5f9e25c-d7d6-472e-b568-4ecb12433028', }], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字', detail: '包含想擷取之字元的文字字串。' }, + startNum: { name: '開始位置', detail: '要在文字中擷取之第一個字元的位置。' }, + numBytes: { name: '字節數', detail: '指定要 MIDB 擷取的字元數目,以位元組為單位。' }, }, }, NUMBERVALUE: { @@ -343,7 +343,7 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是以引號括住的文字、傳回文字的公式,或包含要將部分變為大寫之文字的儲存格參照。' }, + text: { name: '文字', detail: '以引號括住的文字、傳回文字的公式,或包含要將部分變為大寫之文字的儲存格參照。' }, }, }, REGEXEXTRACT: { @@ -399,8 +399,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: '舊文字', detail: '要替換其中某些字元的文字。' }, + startNum: { name: '開始位置', detail: '要在文字中替換的第一個字元的位置。' }, + numChars: { name: '字元數', detail: '指定要 REPLACE 替換的字元數目。' }, + newText: { name: '替換文字', detail: '在舊文字中要替換字元的文字。' }, }, }, REPLACEB: { @@ -413,8 +415,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + oldText: { name: '舊文字', detail: '要替換其中某些字元的文字。' }, + startNum: { name: '開始位置', detail: '要在文字中替換的第一個字元的位置。' }, + numBytes: { name: '字節數', detail: '指定要 REPLACEB 替換的字元數目,以位元組為單位。' }, + newText: { name: '替換文字', detail: '在舊文字中要替換字元的文字。' }, }, }, REPT: { @@ -441,8 +445,8 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是包含您想擷取之字元的文字字串。' }, - numChars: { name: '字元數', detail: '這會指定您要 RIGHT 擷取的字元數目。' }, + text: { name: '文字', detail: '包含想擷取之字元的文字字串。' }, + numChars: { name: '字元數', detail: '指定要 RIGHT 擷取的字元數目。' }, }, }, RIGHTB: { @@ -455,8 +459,8 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是包含您想擷取之字元的文字字串。' }, - numBytes: { name: '字節數', detail: '這會指定要 RIGHTB 擷取的字元數目,以位元組為單位。' }, + text: { name: '文字', detail: '包含想擷取之字元的文字字串。' }, + numBytes: { name: '字節數', detail: '指定要 RIGHTB 擷取的字元數目,以位元組為單位。' }, }, }, SEARCH: { @@ -469,8 +473,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜尋字串', detail: '要在“要搜尋的文字”中尋找的字串。' }, + withinText: { name: '要搜尋的文字', detail: '要搜尋“搜尋字串”的首次出現的文字。' }, + startNum: { name: '開始位置', detail: '要在“要搜尋的文字”中開始搜尋的字元位置。若省略則假定其值為 1。' }, }, }, SEARCHB: { @@ -483,8 +488,9 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + findText: { name: '搜尋字串', detail: '要在“要搜尋的文字”中尋找的字串。' }, + withinText: { name: '要搜尋的文字', detail: '要搜尋“搜尋字串”的首次出現的文字。' }, + startNum: { name: '開始位置', detail: '要在“要搜尋的文字”中開始搜尋的字元位置。若省略則假定其值為 1。' }, }, }, SUBSTITUTE: { @@ -497,10 +503,10 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '這是包含要以字元取代文字的文字或參照。' }, - oldText: { name: '搜尋文字', detail: '這是要取代的文字。' }, - newText: { name: '取代文字', detail: '這是要用來取代 old_text 的文字。' }, - instanceNum: { name: '指定取代對象', detail: '指定要將第幾個 old_text 取代為 new_text。 如果您指定 instance_num,則只會取代該 old_text。 否則,text 中的每一個 old_text 都會變更為 new_text。' }, + text: { name: '文字', detail: '包含要以字元取代文字的文字或參照。' }, + oldText: { name: '搜尋文字', detail: '要取代的文字。' }, + newText: { name: '取代文字', detail: '要用來取代 old_text 的文字。' }, + instanceNum: { name: '指定取代對象', detail: '指定要將第幾個 old_text 取代為 new_text。 如果指定 instance_num,則只會取代該 old_text。 否則,text 中的每一個 old_text 都會變更為 new_text。' }, }, }, T: { @@ -513,7 +519,7 @@ export default { }, ], functionParameter: { - value: { name: '值', detail: '這是要檢定的值。' }, + value: { name: '值', detail: '要檢定的值。' }, }, }, TEXT: { @@ -576,8 +582,10 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + delimiter: { name: '分隔符號', detail: '文字字串,可以是空白、雙引號括起來的一或多個字元,或是有效文字字串的參照。' }, + ignoreEmpty: { name: '忽略空白', detail: '如果為 TRUE,則會忽略空白儲存格。' }, + text1: { name: '文字1', detail: '要加入的文字項目。 文字字串或字串陣列,例如儲存格範圍。' }, + text2: { name: '文字2', detail: '要加入的其他文字項目。 文字項目最多可有 252 個文字引數,包含 text1。 每個項目可以是文字字串或字串陣列,例如儲存格範圍。' }, }, }, TEXTSPLIT: { @@ -599,7 +607,7 @@ export default { }, }, TRIM: { - description: '刪除文字中的空格', + description: '刪除文字的所有空格,僅保留單字之間的單個空格。', abstract: '刪除文字中的空格', links: [ { @@ -608,8 +616,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字', detail: '要從中刪除空格的文字。' }, }, }, UNICHAR: { @@ -635,7 +642,7 @@ export default { }, ], functionParameter: { - text: { name: '文字', detail: '是您要使用 Unicode 值的字元。' }, + text: { name: '文字', detail: '是要使用 Unicode 值的字元。' }, }, }, UPPER: { @@ -648,8 +655,7 @@ export default { }, ], functionParameter: { - number1: { name: 'number1', detail: 'first' }, - number2: { name: 'number2', detail: 'second' }, + text: { name: '文字', detail: '要轉換成大寫的文字。' }, }, }, VALUE: { diff --git a/packages/sheets-formula/src/services/function-list/text.ts b/packages/sheets-formula/src/services/function-list/text.ts index dafa408ce12..277fe19e872 100644 --- a/packages/sheets-formula/src/services/function-list/text.ts +++ b/packages/sheets-formula/src/services/function-list/text.ts @@ -224,19 +224,26 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.FIND.abstract', functionParameter: [ { - name: 'formula.functionList.FIND.functionParameter.number1.name', - detail: 'formula.functionList.FIND.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.FIND.functionParameter.findText.name', + detail: 'formula.functionList.FIND.functionParameter.findText.detail', + example: '"Univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.FIND.functionParameter.number2.name', - detail: 'formula.functionList.FIND.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.FIND.functionParameter.withinText.name', + detail: 'formula.functionList.FIND.functionParameter.withinText.detail', + example: '"Hello Univer"', require: 1, repeat: 0, }, + { + name: 'formula.functionList.FIND.functionParameter.startNum.name', + detail: 'formula.functionList.FIND.functionParameter.startNum.detail', + example: '1', + require: 0, + repeat: 0, + }, ], }, { @@ -246,19 +253,26 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.FINDB.abstract', functionParameter: [ { - name: 'formula.functionList.FINDB.functionParameter.number1.name', - detail: 'formula.functionList.FINDB.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.FINDB.functionParameter.findText.name', + detail: 'formula.functionList.FINDB.functionParameter.findText.detail', + example: '"Univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.FINDB.functionParameter.number2.name', - detail: 'formula.functionList.FINDB.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.FINDB.functionParameter.withinText.name', + detail: 'formula.functionList.FINDB.functionParameter.withinText.detail', + example: '"Hello Univer"', require: 1, repeat: 0, }, + { + name: 'formula.functionList.FINDB.functionParameter.startNum.name', + detail: 'formula.functionList.FINDB.functionParameter.startNum.detail', + example: '1', + require: 0, + repeat: 0, + }, ], }, { @@ -297,17 +311,17 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.LEFT.abstract', functionParameter: [ { - name: 'formula.functionList.LEFT.functionParameter.number1.name', - detail: 'formula.functionList.LEFT.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.LEFT.functionParameter.text.name', + detail: 'formula.functionList.LEFT.functionParameter.text.detail', + example: '"Univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.LEFT.functionParameter.number2.name', - detail: 'formula.functionList.LEFT.functionParameter.number2.detail', - example: 'A1:A20', - require: 1, + name: 'formula.functionList.LEFT.functionParameter.numChars.name', + detail: 'formula.functionList.LEFT.functionParameter.numChars.detail', + example: '3', + require: 0, repeat: 0, }, ], @@ -321,14 +335,14 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ { name: 'formula.functionList.LEFTB.functionParameter.text.name', detail: 'formula.functionList.LEFTB.functionParameter.text.detail', - example: '"Hello Univer"', + example: '"Univer"', require: 1, repeat: 0, }, { name: 'formula.functionList.LEFTB.functionParameter.numBytes.name', detail: 'formula.functionList.LEFTB.functionParameter.numBytes.detail', - example: '1', + example: '3', require: 0, repeat: 0, }, @@ -388,7 +402,7 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ { name: 'formula.functionList.MID.functionParameter.text.name', detail: 'formula.functionList.MID.functionParameter.text.detail', - example: '"Hello Univer"', + example: '"Univer"', require: 1, repeat: 0, }, @@ -402,7 +416,7 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ { name: 'formula.functionList.MID.functionParameter.numChars.name', detail: 'formula.functionList.MID.functionParameter.numChars.detail', - example: '1', + example: '3', require: 1, repeat: 0, }, @@ -415,16 +429,23 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.MIDB.abstract', functionParameter: [ { - name: 'formula.functionList.MIDB.functionParameter.number1.name', - detail: 'formula.functionList.MIDB.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.MIDB.functionParameter.text.name', + detail: 'formula.functionList.MIDB.functionParameter.text.detail', + example: '"Univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.MIDB.functionParameter.number2.name', - detail: 'formula.functionList.MIDB.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.MIDB.functionParameter.startNum.name', + detail: 'formula.functionList.MIDB.functionParameter.startNum.detail', + example: '1', + require: 1, + repeat: 0, + }, + { + name: 'formula.functionList.MIDB.functionParameter.numBytes.name', + detail: 'formula.functionList.MIDB.functionParameter.numBytes.detail', + example: '3', require: 1, repeat: 0, }, @@ -576,16 +597,30 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.REPLACE.abstract', functionParameter: [ { - name: 'formula.functionList.REPLACE.functionParameter.number1.name', - detail: 'formula.functionList.REPLACE.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.REPLACE.functionParameter.oldText.name', + detail: 'formula.functionList.REPLACE.functionParameter.oldText.detail', + example: '"Univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.REPLACE.functionParameter.number2.name', - detail: 'formula.functionList.REPLACE.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.REPLACE.functionParameter.startNum.name', + detail: 'formula.functionList.REPLACE.functionParameter.startNum.detail', + example: '1', + require: 1, + repeat: 0, + }, + { + name: 'formula.functionList.REPLACE.functionParameter.numChars.name', + detail: 'formula.functionList.REPLACE.functionParameter.numChars.detail', + example: '0', + require: 1, + repeat: 0, + }, + { + name: 'formula.functionList.REPLACE.functionParameter.newText.name', + detail: 'formula.functionList.REPLACE.functionParameter.newText.detail', + example: '"Hello "', require: 1, repeat: 0, }, @@ -598,16 +633,30 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.REPLACEB.abstract', functionParameter: [ { - name: 'formula.functionList.REPLACEB.functionParameter.number1.name', - detail: 'formula.functionList.REPLACEB.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.REPLACEB.functionParameter.oldText.name', + detail: 'formula.functionList.REPLACEB.functionParameter.oldText.detail', + example: '"Univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.REPLACEB.functionParameter.number2.name', - detail: 'formula.functionList.REPLACEB.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.REPLACEB.functionParameter.startNum.name', + detail: 'formula.functionList.REPLACEB.functionParameter.startNum.detail', + example: '1', + require: 1, + repeat: 0, + }, + { + name: 'formula.functionList.REPLACEB.functionParameter.numBytes.name', + detail: 'formula.functionList.REPLACEB.functionParameter.numBytes.detail', + example: '0', + require: 1, + repeat: 0, + }, + { + name: 'formula.functionList.REPLACEB.functionParameter.newText.name', + detail: 'formula.functionList.REPLACEB.functionParameter.newText.detail', + example: '"Hello "', require: 1, repeat: 0, }, @@ -686,19 +735,26 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.SEARCH.abstract', functionParameter: [ { - name: 'formula.functionList.SEARCH.functionParameter.number1.name', - detail: 'formula.functionList.SEARCH.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.SEARCH.functionParameter.findText.name', + detail: 'formula.functionList.SEARCH.functionParameter.findText.detail', + example: '"univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.SEARCH.functionParameter.number2.name', - detail: 'formula.functionList.SEARCH.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.SEARCH.functionParameter.withinText.name', + detail: 'formula.functionList.SEARCH.functionParameter.withinText.detail', + example: '"Hello Univer"', require: 1, repeat: 0, }, + { + name: 'formula.functionList.SEARCH.functionParameter.startNum.name', + detail: 'formula.functionList.SEARCH.functionParameter.startNum.detail', + example: '1', + require: 0, + repeat: 0, + }, ], }, { @@ -708,19 +764,26 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.SEARCHB.abstract', functionParameter: [ { - name: 'formula.functionList.SEARCHB.functionParameter.number1.name', - detail: 'formula.functionList.SEARCHB.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.SEARCHB.functionParameter.findText.name', + detail: 'formula.functionList.SEARCHB.functionParameter.findText.detail', + example: '"univer"', require: 1, repeat: 0, }, { - name: 'formula.functionList.SEARCHB.functionParameter.number2.name', - detail: 'formula.functionList.SEARCHB.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.SEARCHB.functionParameter.withinText.name', + detail: 'formula.functionList.SEARCHB.functionParameter.withinText.detail', + example: '"Hello Univer"', require: 1, repeat: 0, }, + { + name: 'formula.functionList.SEARCHB.functionParameter.startNum.name', + detail: 'formula.functionList.SEARCHB.functionParameter.startNum.detail', + example: '1', + require: 0, + repeat: 0, + }, ], }, { @@ -903,19 +966,33 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.TEXTJOIN.abstract', functionParameter: [ { - name: 'formula.functionList.TEXTJOIN.functionParameter.number1.name', - detail: 'formula.functionList.TEXTJOIN.functionParameter.number1.detail', - example: 'A1:A20', + name: 'formula.functionList.TEXTJOIN.functionParameter.delimiter.name', + detail: 'formula.functionList.TEXTJOIN.functionParameter.delimiter.detail', + example: '", "', require: 1, repeat: 0, }, { - name: 'formula.functionList.TEXTJOIN.functionParameter.number2.name', - detail: 'formula.functionList.TEXTJOIN.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.TEXTJOIN.functionParameter.ignoreEmpty.name', + detail: 'formula.functionList.TEXTJOIN.functionParameter.ignoreEmpty.detail', + example: 'true', + require: 1, + repeat: 0, + }, + { + name: 'formula.functionList.TEXTJOIN.functionParameter.text1.name', + detail: 'formula.functionList.TEXTJOIN.functionParameter.text1.detail', + example: '"Hi"', require: 1, repeat: 0, }, + { + name: 'formula.functionList.TEXTJOIN.functionParameter.text2.name', + detail: 'formula.functionList.TEXTJOIN.functionParameter.text2.detail', + example: '"Univer"', + require: 0, + repeat: 1, + }, ], }, { @@ -975,16 +1052,9 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.TRIM.abstract', functionParameter: [ { - name: 'formula.functionList.TRIM.functionParameter.number1.name', - detail: 'formula.functionList.TRIM.functionParameter.number1.detail', - example: 'A1:A20', - require: 1, - repeat: 0, - }, - { - name: 'formula.functionList.TRIM.functionParameter.number2.name', - detail: 'formula.functionList.TRIM.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.TRIM.functionParameter.text.name', + detail: 'formula.functionList.TRIM.functionParameter.text.detail', + example: '" Hello Univer "', require: 1, repeat: 0, }, @@ -1027,16 +1097,9 @@ export const FUNCTION_LIST_TEXT: IFunctionInfo[] = [ abstract: 'formula.functionList.UPPER.abstract', functionParameter: [ { - name: 'formula.functionList.UPPER.functionParameter.number1.name', - detail: 'formula.functionList.UPPER.functionParameter.number1.detail', - example: 'A1:A20', - require: 1, - repeat: 0, - }, - { - name: 'formula.functionList.UPPER.functionParameter.number2.name', - detail: 'formula.functionList.UPPER.functionParameter.number2.detail', - example: 'A1:A20', + name: 'formula.functionList.UPPER.functionParameter.text.name', + detail: 'formula.functionList.UPPER.functionParameter.text.detail', + example: '"Univer"', require: 1, repeat: 0, },