5357 lines
174 KiB
JavaScript
5357 lines
174 KiB
JavaScript
/*
|
||
* (c) Copyright Ascensio System SIA 2010-2024
|
||
*
|
||
* This program is a free software product. You can redistribute it and/or
|
||
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
||
* version 3 as published by the Free Software Foundation. In accordance with
|
||
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
||
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
||
* of any third-party rights.
|
||
*
|
||
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
||
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||
*
|
||
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
||
* street, Riga, Latvia, EU, LV-1050.
|
||
*
|
||
* The interactive user interfaces in modified source and object code versions
|
||
* of the Program must display Appropriate Legal Notices, as required under
|
||
* Section 5 of the GNU AGPL version 3.
|
||
*
|
||
* Pursuant to Section 7(b) of the License you must retain the original Product
|
||
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
||
* grant you any rights under trademark law for use of our trademarks.
|
||
*
|
||
* All the Product's GUI elements, including illustrations and icon sets, as
|
||
* well as technical writing content are licensed under the terms of the
|
||
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||
*
|
||
*/
|
||
|
||
"use strict";
|
||
|
||
(/**
|
||
* @param {Window} window
|
||
* @param {undefined} undefined
|
||
*/
|
||
function (window, undefined) {
|
||
var g_cCharDelimiter = AscCommon.g_cCharDelimiter;
|
||
var parserHelp = AscCommon.parserHelp;
|
||
var gc_nMaxRow0 = AscCommon.gc_nMaxRow0;
|
||
var gc_nMaxCol0 = AscCommon.gc_nMaxCol0;
|
||
var g_oCellAddressUtils = AscCommon.g_oCellAddressUtils;
|
||
var CellAddress = AscCommon.CellAddress;
|
||
|
||
var cElementType = AscCommonExcel.cElementType;
|
||
var cElementTypeWeight = AscCommonExcel.cElementTypeWeight;
|
||
var cErrorType = AscCommonExcel.cErrorType;
|
||
var cNumber = AscCommonExcel.cNumber;
|
||
var cString = AscCommonExcel.cString;
|
||
var cBool = AscCommonExcel.cBool;
|
||
var cError = AscCommonExcel.cError;
|
||
var cArea = AscCommonExcel.cArea;
|
||
var cArea3D = AscCommonExcel.cArea3D;
|
||
var cRef = AscCommonExcel.cRef;
|
||
var cRef3D = AscCommonExcel.cRef3D;
|
||
var cName = AscCommonExcel.cName;
|
||
var cName3D = AscCommonExcel.cName3D;
|
||
var cEmpty = AscCommonExcel.cEmpty;
|
||
var cArray = AscCommonExcel.cArray;
|
||
var cBaseFunction = AscCommonExcel.cBaseFunction;
|
||
|
||
var checkTypeCell = AscCommonExcel.checkTypeCell;
|
||
var cFormulaFunctionGroup = AscCommonExcel.cFormulaFunctionGroup;
|
||
var argType = Asc.c_oAscFormulaArgumentType;
|
||
|
||
var _func = AscCommonExcel._func;
|
||
|
||
cFormulaFunctionGroup['LookupAndReference'] = cFormulaFunctionGroup['LookupAndReference'] || [];
|
||
cFormulaFunctionGroup['LookupAndReference'].push(cADDRESS, cAREAS, cCHOOSE, cCHOOSECOLS, cCHOOSEROWS, cCOLUMN, cCOLUMNS, cDROP, cEXPAND, cFILTER, cFORMULATEXT,
|
||
cGETPIVOTDATA, cHLOOKUP, cHYPERLINK, cINDEX, cINDIRECT, cLOOKUP, cMATCH, cOFFSET, cROW, cROWS, cSORT, cSORTBY, cRTD, cTRANSPOSE, cTAKE,
|
||
cUNIQUE, cVLOOKUP, cXLOOKUP, cVSTACK, cHSTACK, cTOROW, cTOCOL, cWRAPROWS, cWRAPCOLS, cXMATCH);
|
||
|
||
cFormulaFunctionGroup['NotRealised'] = cFormulaFunctionGroup['NotRealised'] || [];
|
||
cFormulaFunctionGroup['NotRealised'].push(cRTD);
|
||
|
||
function searchRegExp(str, flags) {
|
||
var vFS = str
|
||
.replace(/(\\)/g, "\\")
|
||
.replace(/(\^)/g, "\\^")
|
||
.replace(/(\()/g, "\\(")
|
||
.replace(/(\))/g, "\\)")
|
||
.replace(/(\+)/g, "\\+")
|
||
.replace(/(\[)/g, "\\[")
|
||
.replace(/(\])/g, "\\]")
|
||
.replace(/(\{)/g, "\\{")
|
||
.replace(/(\})/g, "\\}")
|
||
.replace(/(\$)/g, "\\$")
|
||
.replace(/(~)?\*/g, function ($0, $1) {
|
||
return $1 ? $0 : '(.*)';
|
||
})
|
||
.replace(/(~)?\?/g, function ($0, $1) {
|
||
return $1 ? $0 : '.{1}';
|
||
})
|
||
.replace(/(~\*)/g, "\\*").replace(/(~\?)/g, "\\?");
|
||
|
||
return new RegExp(vFS + "$", flags ? flags : "i");
|
||
}
|
||
|
||
// TODO переделать поиск: добиться такого же результата как в ms
|
||
function XBinarySearch (target, array, match_mode, isReverse) {
|
||
let mid, item;
|
||
let index = -1;
|
||
let leftPointer = 0,
|
||
rightPointer = array.length - 1;
|
||
|
||
function bidirectionalSearch (secondIteration, isDescending) {
|
||
while(leftPointer <= rightPointer) {
|
||
mid = Math.floor((leftPointer + rightPointer) / 2);
|
||
item = undefined !== array[mid].v ? array[mid].v : array[mid];
|
||
if(-1 === match_mode || 0 === match_mode || 1 === match_mode) {
|
||
if (leftPointer === 0 && rightPointer === array.length - 1 && !secondIteration) {
|
||
if ((isDescending && target >= item) || (!isDescending && target <= item)) {
|
||
rightPointer = mid - 1;
|
||
} else {
|
||
leftPointer = mid + 1;
|
||
}
|
||
} else if(!secondIteration) {
|
||
if (target == item) {
|
||
index = mid;
|
||
break;
|
||
} else if ((isDescending && target > item) || (!isDescending && target < item)) {
|
||
rightPointer = mid - 1;
|
||
} else {
|
||
leftPointer = mid + 1;
|
||
}
|
||
}
|
||
|
||
if(secondIteration) {
|
||
// exact
|
||
if (0 === match_mode) {
|
||
if(item == target) {
|
||
index = mid;
|
||
break;
|
||
} else if(item < target) {
|
||
if (isDescending) {
|
||
rightPointer = mid - 1;
|
||
} else {
|
||
leftPointer = mid + 1;
|
||
}
|
||
} else {
|
||
if (isDescending) {
|
||
leftPointer = mid + 1
|
||
} else {
|
||
rightPointer = mid - 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// exact or larger
|
||
if (1 === match_mode) {
|
||
if (leftPointer === 0 && rightPointer === array.length - 1) {
|
||
if (item == target) {
|
||
index = mid;
|
||
break;
|
||
} else if (item < target) {
|
||
if (isDescending) {
|
||
rightPointer = mid - 1;
|
||
} else {
|
||
leftPointer = mid + 1;
|
||
}
|
||
} else {
|
||
if (isDescending) {
|
||
leftPointer = mid + 1;
|
||
} else {
|
||
rightPointer = mid - 1;
|
||
}
|
||
}
|
||
} else {
|
||
if (item > target || item == target) {
|
||
index = mid;
|
||
break;
|
||
} else {
|
||
if (isDescending) {
|
||
rightPointer = mid - 1;
|
||
} else {
|
||
leftPointer = mid + 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// exact or smaller
|
||
if (-1 === match_mode) {
|
||
if (leftPointer === 0 && rightPointer === array.length - 1) {
|
||
if(item == target) {
|
||
index = mid;
|
||
break;
|
||
} else if (item < target) {
|
||
if (isDescending) {
|
||
rightPointer = mid - 1;
|
||
} else {
|
||
leftPointer = mid + 1;
|
||
}
|
||
} else {
|
||
if (isDescending) {
|
||
leftPointer = mid + 1;
|
||
} else {
|
||
rightPointer = mid - 1;
|
||
}
|
||
}
|
||
} else {
|
||
if (item < target || item == target) {
|
||
index = mid;
|
||
break;
|
||
} else {
|
||
if (isDescending) {
|
||
leftPointer = mid + 1;
|
||
} else {
|
||
rightPointer = mid - 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
bidirectionalSearch(false, isReverse)
|
||
|
||
// second iteration
|
||
if(index === -1 && (1 === match_mode || -1 === match_mode || 0 === match_mode)) {
|
||
leftPointer = 0;
|
||
rightPointer = array.length - 1;
|
||
bidirectionalSearch(true, isReverse);
|
||
}
|
||
|
||
return index;
|
||
}
|
||
|
||
// these functions are made and used specifically for SORT & SORTBY functions
|
||
function sortWithIndices (arr, sortOrder, isByCol) {
|
||
const indexedArray = isByCol
|
||
? arr[0].map(function (item, index) { return { item: item, index: index } })
|
||
: arr.map(function (item, index) {
|
||
item = item[0];
|
||
return { item: item, index: index };
|
||
});
|
||
|
||
indexedArray.sort(function (a, b) {
|
||
const itemA = a.item;
|
||
const itemB = b.item;
|
||
|
||
let res = 0;
|
||
if (cElementType.string === itemA.type && cElementType.string === itemB.type) {
|
||
res = AscCommonExcel.stringCompare(itemA.value, itemB.value) * sortOrder;
|
||
} else if (cElementType.number === itemA.type && cElementType.number === itemB.type) {
|
||
res = (itemA.value - itemB.value) * sortOrder;
|
||
} else if (cElementType.string === itemA.type) {
|
||
// check itemB.type and make decision
|
||
if (cElementType.number === itemB.type) {
|
||
res = 1 * sortOrder;
|
||
} else if (cElementType.bool === itemB.type || cElementType.error === itemB.type || cElementType.empty === itemB.type) {
|
||
res = -1 * sortOrder;
|
||
}
|
||
} else if (cElementType.string === itemB.type) {
|
||
// check itemA.type and make decision
|
||
if (cElementType.number === itemA.type) {
|
||
res = -1 * sortOrder;
|
||
} else if (cElementType.bool === itemA.type || cElementType.error === itemA.type || cElementType.empty === itemA.type) {
|
||
res = 1 * sortOrder;
|
||
}
|
||
} else if (cElementType.bool === itemA.type) {
|
||
if (cElementType.error === itemB.type || cElementType.empty === itemB.type) {
|
||
res = -1 * sortOrder;
|
||
} else {
|
||
res = 1 *sortOrder;
|
||
}
|
||
} else if (cElementType.bool === itemB.type) {
|
||
if (cElementType.error === itemA.type || cElementType.empty === itemA.type) {
|
||
res = 1 * sortOrder;
|
||
} else {
|
||
res = -1 *sortOrder;
|
||
}
|
||
} else if (cElementType.error === itemA.type) {
|
||
if (cElementType.error === itemB.type) {
|
||
res = 1 * sortOrder;
|
||
} else if (cElementType.empty === itemB.type) {
|
||
res = -1 * sortOrder;
|
||
} else {
|
||
res = 1 * sortOrder;
|
||
}
|
||
} else if (cElementType.error === itemB.type) {
|
||
if (cElementType.error === itemA.type) {
|
||
res = -1 * sortOrder;
|
||
} else if (cElementType.empty === itemA.type) {
|
||
res = 1 * sortOrder;
|
||
} else {
|
||
res = -1 * sortOrder;
|
||
}
|
||
} else if (cElementType.empty === itemA.type || cElementType.empty === itemB.type) {
|
||
res = 1 * sortOrder;
|
||
} else {
|
||
res = 0;
|
||
}
|
||
|
||
return res;
|
||
});
|
||
|
||
return indexedArray;
|
||
}
|
||
|
||
// these functions are made and used specifically for SORT & SORTBY functions
|
||
function sortArray (array, by_array1, sortOrder, isByCol, sortIndex) {
|
||
let resultArr = new cArray(),
|
||
tempArrIndicies = [],
|
||
byRowColArr, targetElem;
|
||
|
||
if (by_array1) {
|
||
byRowColArr = isByCol ? by_array1._getRow(0) : by_array1._getCol(0);
|
||
} else {
|
||
targetElem = isByCol ? array._getRow(sortIndex - 1) : array._getCol(sortIndex - 1);
|
||
}
|
||
|
||
// sorting an array with indices
|
||
tempArrIndicies = sortWithIndices(byRowColArr ? byRowColArr : targetElem, sortOrder, isByCol);
|
||
|
||
for (let i = 0; i < tempArrIndicies.length; i++) {
|
||
let target = isByCol ? array._getCol(tempArrIndicies[i].index) : array._getRow(tempArrIndicies[i].index);
|
||
isByCol ? resultArr.pushCol(target, 0) : resultArr.pushRow(target, 0);
|
||
}
|
||
|
||
return resultArr;
|
||
}
|
||
|
||
function sortByArrayWrapper (array, args, isByCol) {
|
||
let colsRowArr = [], sortOrderArr = [], colsRowIndexesArr = [], rowCol;
|
||
|
||
for (let i = 1; i < args.length; i += 2) {
|
||
let by_array = args[i],
|
||
sortOrder = args[i+1];
|
||
|
||
let dim = by_array.getDimensions();
|
||
// check column or row
|
||
if (dim.bbox) {
|
||
rowCol = isByCol ? dim.bbox.r1 : dim.bbox.c1;
|
||
} else {
|
||
// add array/range check for mainArray and by_array args
|
||
rowCol = isByCol ? dim.col : dim.row;
|
||
}
|
||
|
||
// if there is no such column/row yet, push into the array
|
||
if (colsRowIndexesArr.indexOf(rowCol) === -1) {
|
||
colsRowIndexesArr.push(rowCol);
|
||
sortOrderArr.push(sortOrder);
|
||
colsRowArr.push(isByCol ? by_array._getRow(0) : by_array._getCol(0));
|
||
}
|
||
}
|
||
|
||
let tempArrIndicies = sortByArray(colsRowArr, sortOrderArr, isByCol);
|
||
|
||
let resultArr = new cArray();
|
||
for (let i = 0; i < tempArrIndicies.length; i++) {
|
||
let target = isByCol ? array._getCol(tempArrIndicies[i].index) : array._getRow(tempArrIndicies[i].index);
|
||
isByCol ? resultArr.pushCol(target, 0) : resultArr.pushRow(target, 0);
|
||
}
|
||
|
||
return resultArr;
|
||
}
|
||
|
||
function sortByArray (colsRowsArr, sortOrderArr, isByCol) {
|
||
let by_array1 = colsRowsArr[0],
|
||
tempArrIndicies = [];
|
||
|
||
tempArrIndicies = indicesBy(by_array1, isByCol);
|
||
|
||
tempArrIndicies.sort(function (a, b) {
|
||
let res = 0;
|
||
|
||
const compareFunc = function (_a, _b, _sortOrder) {
|
||
let itemA = _a.item ? _a.item : _a,
|
||
itemB = _b.item ? _b.item : _b;
|
||
|
||
if (cElementType.string === itemA.type && cElementType.string === itemB.type) {
|
||
res = AscCommonExcel.stringCompare(itemA.value, itemB.value) * _sortOrder;
|
||
} else if (cElementType.number === itemA.type && cElementType.number === itemB.type) {
|
||
res = (itemA.value - itemB.value) * _sortOrder;
|
||
} else if (cElementType.string === itemA.type) {
|
||
// check itemB.type and make decision
|
||
if (cElementType.number === itemB.type) {
|
||
res = 1 * _sortOrder;
|
||
} else if (cElementType.bool === itemB.type || cElementType.error === itemB.type) {
|
||
res = -1 * _sortOrder;
|
||
}
|
||
} else if (cElementType.string === itemB.type) {
|
||
// check itemA.type and make decision
|
||
if (cElementType.number === itemA.type) {
|
||
res = -1 * _sortOrder;
|
||
} else if (cElementType.bool === itemA.type || cElementType.error === itemA.type) {
|
||
res = -1 * _sortOrder;
|
||
}
|
||
} else if (cElementType.bool === itemA.type) {
|
||
if (cElementType.error === itemB.type) {
|
||
res = -1 * _sortOrder;
|
||
} else {
|
||
res = 1 *_sortOrder;
|
||
}
|
||
} else if (cElementType.bool === itemB.type) {
|
||
if (cElementType.error === itemA.type) {
|
||
res = 1 * _sortOrder;
|
||
} else {
|
||
res = -1 *_sortOrder;
|
||
}
|
||
} else if (cElementType.error === itemA.type) {
|
||
res = 1 * _sortOrder;
|
||
} else if (cElementType.error === itemA.type) {
|
||
res = 1 * _sortOrder;
|
||
} else {
|
||
res = 0;
|
||
}
|
||
}
|
||
|
||
compareFunc(a, b, sortOrderArr[0]);
|
||
|
||
if (res === 0) {
|
||
for (let i = 1; i < colsRowsArr.length; i++) {
|
||
let tempA = isByCol ? colsRowsArr[i][0][a.index] : colsRowsArr[i][a.index][0];
|
||
let tempB = isByCol ? colsRowsArr[i][0][b.index] : colsRowsArr[i][b.index][0];
|
||
|
||
compareFunc(tempA, tempB, sortOrderArr[i]);
|
||
|
||
if (res !== 0) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return res;
|
||
});
|
||
|
||
return tempArrIndicies;
|
||
}
|
||
|
||
function indicesBy (arr, isByCol) {
|
||
const indexedArray = isByCol
|
||
? arr[0].map(function (item, index) {
|
||
return { item: item, index: index };
|
||
})
|
||
: arr.map(function (item, index) {
|
||
item = item[0];
|
||
return { item: item, index: index };
|
||
});
|
||
|
||
return indexedArray;
|
||
}
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cADDRESS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cADDRESS.prototype = Object.create(cBaseFunction.prototype);
|
||
cADDRESS.prototype.constructor = cADDRESS;
|
||
cADDRESS.prototype.name = 'ADDRESS';
|
||
cADDRESS.prototype.argumentsMin = 2;
|
||
cADDRESS.prototype.argumentsMax = 5;
|
||
cADDRESS.prototype.argumentsType = [argType.number, argType.number, argType.number, argType.logical, argType.text];
|
||
cADDRESS.prototype.Calculate = function (arg) {
|
||
var rowNumber = arg[0], colNumber = arg[1], refType = arg[2] ? arg[2] : new cNumber(1),
|
||
A1RefType = arg[3] ? arg[3] : new cBool(true), sheetName = arg[4] ? arg[4] : null;
|
||
|
||
if (cElementType.cellsRange === rowNumber.type || cElementType.cellsRange3D === rowNumber.type) {
|
||
rowNumber = rowNumber.cross(arguments[1]);
|
||
} else if (cElementType.array === rowNumber.type) {
|
||
rowNumber = rowNumber.getElementRowCol(0, 0);
|
||
}
|
||
|
||
if (cElementType.cellsRange === colNumber.type || cElementType.cellsRange3D === colNumber.type) {
|
||
colNumber = colNumber.cross(arguments[1]);
|
||
} else if (cElementType.array === colNumber.type) {
|
||
colNumber = colNumber.getElementRowCol(0, 0);
|
||
}
|
||
|
||
if (cElementType.cellsRange === refType.type || cElementType.cellsRange3D === refType.type) {
|
||
refType = refType.cross(arguments[1]);
|
||
} else if (cElementType.array === refType.type) {
|
||
refType = refType.getElementRowCol(0, 0);
|
||
} else if(cElementType.empty === refType.type) {
|
||
refType = new cNumber(1);
|
||
}
|
||
|
||
if (cElementType.cellsRange === A1RefType.type || cElementType.cellsRange3D === A1RefType.type) {
|
||
A1RefType = A1RefType.cross(arguments[1]);
|
||
} else if (cElementType.array === A1RefType.type) {
|
||
A1RefType = A1RefType.getElementRowCol(0, 0);
|
||
} else if(cElementType.empty === A1RefType.type) {
|
||
A1RefType = new cNumber(1);
|
||
}
|
||
|
||
if(sheetName){
|
||
if (cElementType.cellsRange === sheetName.type || cElementType.cellsRange3D === sheetName.type) {
|
||
sheetName = sheetName.cross(arguments[1]);
|
||
} else if (cElementType.array === sheetName.type) {
|
||
sheetName = sheetName.getElementRowCol(0, 0);
|
||
} else if (cElementType.cell === sheetName.type || cElementType.cell3D === sheetName.type) {
|
||
sheetName = sheetName.getValue();
|
||
} else if (cElementType.empty === sheetName.type) {
|
||
sheetName = null;
|
||
}
|
||
}
|
||
|
||
rowNumber = rowNumber.tocNumber();
|
||
colNumber = colNumber.tocNumber();
|
||
refType = refType.tocNumber();
|
||
A1RefType = A1RefType.tocBool();
|
||
|
||
if (cElementType.error === rowNumber.type) {
|
||
return rowNumber;
|
||
}
|
||
if (cElementType.error === colNumber.type) {
|
||
return colNumber;
|
||
}
|
||
if (cElementType.error === refType.type) {
|
||
return refType;
|
||
}
|
||
if (cElementType.error === A1RefType.type) {
|
||
return A1RefType;
|
||
}
|
||
if (sheetName && cElementType.error === sheetName.type) {
|
||
return sheetName;
|
||
}
|
||
|
||
rowNumber = rowNumber.getValue();
|
||
colNumber = colNumber.getValue();
|
||
refType = refType.getValue();
|
||
//TODO tocBool - if !isBoolean -> value error?
|
||
if (!A1RefType.toBool) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
A1RefType = A1RefType.toBool();
|
||
}
|
||
|
||
rowNumber = parseInt(rowNumber);
|
||
colNumber = parseInt(colNumber);
|
||
|
||
if (refType > 4 || refType < 1 || rowNumber < 1 || rowNumber > AscCommon.gc_nMaxRow || colNumber < 1 ||
|
||
colNumber > AscCommon.gc_nMaxCol) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
var strRef;
|
||
var absR, absC;
|
||
switch (refType - 1) {
|
||
case AscCommonExcel.referenceType.A:
|
||
absR = true;
|
||
absC = true;
|
||
break;
|
||
case AscCommonExcel.referenceType.ARRC:
|
||
absR = true;
|
||
absC = false;
|
||
break;
|
||
case AscCommonExcel.referenceType.RRAC:
|
||
absR = false;
|
||
absC = true;
|
||
break;
|
||
case AscCommonExcel.referenceType.R:
|
||
absR = false;
|
||
absC = false;
|
||
break;
|
||
}
|
||
|
||
strRef = this._getRef(this._absolute(absR, rowNumber, A1RefType),
|
||
this._absolute(absC, A1RefType ? g_oCellAddressUtils.colnumToColstrFromWsView(colNumber) : colNumber,
|
||
A1RefType), A1RefType);
|
||
|
||
var res = strRef;
|
||
if(sheetName){
|
||
if("" === sheetName.getValue()){
|
||
res = "!" + strRef;
|
||
} else {
|
||
res = parserHelp.get3DRef(sheetName.toString(), strRef);
|
||
}
|
||
}
|
||
|
||
return new cString(res);
|
||
};
|
||
cADDRESS.prototype._getRef = function (row, col, A1RefType) {
|
||
return A1RefType ? col + row : 'R' + row + 'C' + col;
|
||
};
|
||
cADDRESS.prototype._absolute = function (abs, val, A1RefType) {
|
||
|
||
return abs ? (A1RefType ? '$' + val : val) : (A1RefType ? val : '[' + val + ']');
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cAREAS() {
|
||
}
|
||
|
||
cAREAS.prototype = Object.create(cBaseFunction.prototype);
|
||
cAREAS.prototype.constructor = cAREAS;
|
||
cAREAS.prototype.name = 'AREAS';
|
||
cAREAS.prototype.argumentsMin = 1;
|
||
cAREAS.prototype.argumentsMax = 1;
|
||
cAREAS.prototype.argumentsType = [argType.reference];
|
||
cAREAS.prototype.arrayIndexes = {0: 1};
|
||
cAREAS.prototype.Calculate = function (arg) {
|
||
let arg0 = arg[0];
|
||
if (arg0.type !== AscCommonExcel.cElementType.cell && arg0.type !== AscCommonExcel.cElementType.cell3D &&
|
||
arg0.type !== AscCommonExcel.cElementType.cellsRange && arg0.type !== AscCommonExcel.cElementType.cellsRange3D) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
let areasCounter = 1;
|
||
// todo In future versions, after adding the processing of the brackets inside the argument, make the counter of several areas inside arg
|
||
|
||
return new cNumber(areasCounter);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cCHOOSE() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cCHOOSE.prototype = Object.create(cBaseFunction.prototype);
|
||
cCHOOSE.prototype.constructor = cCHOOSE;
|
||
cCHOOSE.prototype.name = 'CHOOSE';
|
||
cCHOOSE.prototype.argumentsMin = 2;
|
||
cCHOOSE.prototype.argumentsMax = 30;
|
||
// todo add arrayIndex to all n-arguments (in array)
|
||
cCHOOSE.prototype.arrayIndexes = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1};
|
||
cCHOOSE.prototype.argumentsType = [argType.number, [argType.any]];
|
||
cCHOOSE.prototype.Calculate = function (arg) {
|
||
const args = arguments;
|
||
let arg0 = arg[0];
|
||
|
||
const chooseArgument = function (_arg0) {
|
||
if (cElementType.cellsRange === _arg0.type || cElementType.cellsRange3D === _arg0.type) {
|
||
_arg0 = _arg0.cross(args[1]);
|
||
}
|
||
|
||
_arg0 = _arg0.tocNumber();
|
||
if (cElementType.error === _arg0.type) {
|
||
return _arg0;
|
||
}
|
||
|
||
if (cElementType.number === _arg0.type) {
|
||
_arg0 = Math.floor(_arg0.getValue());
|
||
if (_arg0 < 1 || _arg0 > arg.length - 1) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
return arg[Math.floor(_arg0)];
|
||
}
|
||
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if (cElementType.array === arg0.type || cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
// TODO refactor
|
||
// go through the array and return result for each element
|
||
let resArr = new cArray();
|
||
let tempArraySize, maxArraySize = arg0.getDimensions();
|
||
let arg0Rows = maxArraySize.row, arg0Cols = maxArraySize.col;
|
||
|
||
// get max array size by first loop
|
||
arg0.foreach2(function (elem) {
|
||
let chosenArgument = chooseArgument(elem);
|
||
if (chosenArgument && chosenArgument.type === cElementType.cellsRange || chosenArgument.type === cElementType.cellsRange3D || chosenArgument.type === cElementType.array) {
|
||
tempArraySize = chosenArgument.getDimensions();
|
||
maxArraySize.row = tempArraySize.row > maxArraySize.row ? tempArraySize.row : maxArraySize.row;
|
||
maxArraySize.col = tempArraySize.col > maxArraySize.col ? tempArraySize.col : maxArraySize.col;
|
||
}
|
||
});
|
||
|
||
for (let r = 0; r < arg0Rows; r++) {
|
||
for (let c = 0; c < arg0Cols; c++) {
|
||
let elem = arg0.getValue2(r, c);
|
||
let chosenArgument = chooseArgument(elem);
|
||
let argDimensions = chosenArgument.getDimensions();
|
||
let singleRow = arg0Rows === 1;
|
||
let singleCol = arg0Cols === 1;
|
||
let tempArr = [];
|
||
|
||
if (singleRow || singleCol) {
|
||
// if the first argument has one row or column we need to fully take this row or column and pass it to the resulting array
|
||
for (let i = 0; i < (singleRow ? maxArraySize.row : maxArraySize.col); i++) {
|
||
let elemFromChosenArgument;
|
||
if (chosenArgument.type === cElementType.array || chosenArgument.type === cElementType.cellsRange || chosenArgument.type === cElementType.cellsRange3D) {
|
||
if (argDimensions.col === 1) {
|
||
// return elem from first col
|
||
elemFromChosenArgument = chosenArgument.getElementRowCol ? chosenArgument.getElementRowCol(singleRow ? i : r, 0) : chosenArgument.getValueByRowCol(singleRow ? i : r, 0);
|
||
} else if (argDimensions.row === 1) {
|
||
// return elem from first row
|
||
elemFromChosenArgument = chosenArgument.getElementRowCol ? chosenArgument.getElementRowCol(0, singleRow ? c : i) : chosenArgument.getValueByRowCol(0, singleRow ? c : i);
|
||
} else {
|
||
// return r/c elem
|
||
elemFromChosenArgument = chosenArgument.getElementRowCol ? chosenArgument.getElementRowCol(singleRow ? i : r, singleRow ? c : i) : chosenArgument.getValueByRowCol(singleRow ? i : r, singleRow ? c : i);
|
||
}
|
||
|
||
// if we go outside the range, we must return the #N/A error to the array
|
||
if ((singleRow && argDimensions.row - 1 !== 0 && argDimensions.row - 1 < i) || (singleCol && argDimensions.col - 1 !== 0 && argDimensions.col - 1 < i)) {
|
||
elemFromChosenArgument = new cError(cErrorType.not_available);
|
||
}
|
||
} else {
|
||
elemFromChosenArgument = chosenArgument;
|
||
}
|
||
|
||
// undefined can be obtained when accessing an empty cell in the range, in which case we need to return cEmpty
|
||
if (elemFromChosenArgument === undefined) {
|
||
elemFromChosenArgument = new cEmpty();
|
||
}
|
||
|
||
singleRow ? tempArr.push([elemFromChosenArgument]) : tempArr.push(elemFromChosenArgument);
|
||
}
|
||
singleRow ? resArr.pushCol(tempArr, 0) : resArr.pushRow([tempArr], 0);
|
||
} else {
|
||
// get r/c part from chosen argument
|
||
let elemFromChosenArgument;
|
||
if (chosenArgument.type === cElementType.array || chosenArgument.type === cElementType.cellsRange || chosenArgument.type === cElementType.cellsRange3D) {
|
||
if (argDimensions.row === 1) {
|
||
elemFromChosenArgument = chosenArgument.getElementRowCol ? chosenArgument.getElementRowCol(0, c) : chosenArgument.getValueByRowCol(0, c);
|
||
} else if (argDimensions.col === 1) {
|
||
elemFromChosenArgument = chosenArgument.getElementRowCol ? chosenArgument.getElementRowCol(r, 0) : chosenArgument.getValueByRowCol(r, 0);
|
||
} else {
|
||
elemFromChosenArgument = chosenArgument.getElementRowCol ? chosenArgument.getElementRowCol(r, c) : chosenArgument.getValueByRowCol(r, c);
|
||
}
|
||
if (argDimensions.col - 1 !== 0 && argDimensions.col - 1 < c) {
|
||
elemFromChosenArgument = new cError(cErrorType.not_available);
|
||
}
|
||
} else {
|
||
elemFromChosenArgument = chosenArgument;
|
||
}
|
||
|
||
// undefined can be obtained when accessing an empty cell in the range, in which case we need to return cEmpty
|
||
if (elemFromChosenArgument === undefined) {
|
||
elemFromChosenArgument = new cEmpty();
|
||
}
|
||
|
||
if (!resArr.array[r]) {
|
||
resArr.addRow();
|
||
}
|
||
resArr.addElement(elemFromChosenArgument);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (resArr.getRowCount() < maxArraySize.row) {
|
||
// fill the rest of array with #N/A error
|
||
for (let i = resArr.getRowCount(); i < maxArraySize.row; i++) {
|
||
resArr.addRow();
|
||
for (let j = 0; j < resArr.getCountElementInRow(); j++) {
|
||
resArr.addElement(new cError(cErrorType.not_available));
|
||
}
|
||
}
|
||
}
|
||
|
||
return resArr;
|
||
}
|
||
|
||
return chooseArgument(arg0);
|
||
|
||
};
|
||
|
||
function chooseRowsCols(arg, argument1, byCol) {
|
||
var argError = cBaseFunction.prototype._checkErrorArg.call(this, arg);
|
||
if (argError) {
|
||
return argError;
|
||
}
|
||
|
||
let arg1 = arg[0];
|
||
let matrix;
|
||
if (arg1.type === cElementType.cellsRange || arg1.type === cElementType.array || arg1.type === cElementType.cell || arg1.type === cElementType.cell3D) {
|
||
matrix = arg1.getMatrix();
|
||
} else if (arg1.type === cElementType.cellsRange3D) {
|
||
if (arg1.isSingleSheet()) {
|
||
matrix = arg1.getMatrix()[0];
|
||
} else {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
} else if (arg1.type === cElementType.error) {
|
||
return arg1;
|
||
} else if (arg1.type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
matrix = [[arg1]];
|
||
}
|
||
|
||
let pushData = function (_argInside) {
|
||
_argInside = _argInside.tocNumber();
|
||
if (_argInside.type === cElementType.error) {
|
||
error = _argInside;
|
||
return false;
|
||
}
|
||
_argInside = _argInside.toNumber();
|
||
let reverse = _argInside < 0;
|
||
_argInside = Math.abs(_argInside);
|
||
_argInside = parseInt(_argInside);
|
||
if (_argInside < 1 || (_argInside > dimension.col && byCol) || (_argInside > dimension.row && !byCol)) {
|
||
error = new cError(cErrorType.wrong_value_type);
|
||
return false;
|
||
}
|
||
|
||
if (!res) {
|
||
res = new cArray();
|
||
}
|
||
|
||
if (byCol) {
|
||
res.pushCol(matrix, reverse ? dimension.col - (_argInside - 1) - 1 : _argInside - 1);
|
||
} else {
|
||
res.pushRow(matrix, reverse ? dimension.row - (_argInside - 1) - 1 : _argInside - 1);
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
let dimension = arg1.getDimensions();
|
||
let res;
|
||
let error;
|
||
for (let i = 1; i < arg.length; i++) {
|
||
let _arg = arg[i];
|
||
|
||
if (cElementType.cellsRange === _arg.type || cElementType.cellsRange3D === _arg.type || cElementType.array === _arg.type) {
|
||
let argDimensions = _arg.getDimensions();
|
||
if (argDimensions.col === 1 || argDimensions.row === 1) {
|
||
let byCol = argDimensions.row > 1;
|
||
for (let j = 0; j < Math.max(argDimensions.col, argDimensions.row); j++) {
|
||
if (cElementType.array === _arg.type) {
|
||
if (!pushData(_arg.getElementRowCol(!byCol ? 0 : j, !byCol ? j : 0))) {
|
||
return error;
|
||
}
|
||
} else {
|
||
if (!pushData(_arg.getValue2(!byCol ? 0 : j, !byCol ? j : 0))) {
|
||
return error;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
if (!pushData(_arg)) {
|
||
return error;
|
||
}
|
||
}
|
||
|
||
return res ? res : new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cCHOOSECOLS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cCHOOSECOLS.prototype = Object.create(cBaseFunction.prototype);
|
||
cCHOOSECOLS.prototype.constructor = cCHOOSECOLS;
|
||
cCHOOSECOLS.prototype.name = 'CHOOSECOLS';
|
||
cCHOOSECOLS.prototype.argumentsMin = 2;
|
||
cCHOOSECOLS.prototype.argumentsMax = 253;
|
||
cCHOOSECOLS.prototype.argumentsType = [argType.reference, [argType.number]];
|
||
cCHOOSECOLS.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cCHOOSECOLS.prototype.isXLFN = true;
|
||
cCHOOSECOLS.prototype.Calculate = function (arg) {
|
||
return chooseRowsCols(arg, arguments[1], true);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cCHOOSEROWS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cCHOOSEROWS.prototype = Object.create(cBaseFunction.prototype);
|
||
cCHOOSEROWS.prototype.constructor = cCHOOSEROWS;
|
||
cCHOOSEROWS.prototype.name = 'CHOOSEROWS';
|
||
cCHOOSEROWS.prototype.argumentsMin = 2;
|
||
cCHOOSEROWS.prototype.argumentsMax = 253;
|
||
cCHOOSEROWS.prototype.argumentsType = [argType.reference, [argType.number]];
|
||
cCHOOSEROWS.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cCHOOSEROWS.prototype.isXLFN = true;
|
||
cCHOOSEROWS.prototype.Calculate = function (arg) {
|
||
return chooseRowsCols(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cCOLUMN() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cCOLUMN.prototype = Object.create(cBaseFunction.prototype);
|
||
cCOLUMN.prototype.constructor = cCOLUMN;
|
||
cCOLUMN.prototype.name = 'COLUMN';
|
||
cCOLUMN.prototype.argumentsMax = 1;
|
||
cCOLUMN.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.setArrayRefAsArg;
|
||
cCOLUMN.prototype.argumentsType = [argType.reference];
|
||
cCOLUMN.prototype.Calculate = function (arg) {
|
||
var bbox;
|
||
var res;
|
||
var opt_col = arguments[6];
|
||
if (opt_col !== undefined) {
|
||
return new cNumber(opt_col + 1);
|
||
} else if (0 === arg.length) {
|
||
bbox = arguments[1];
|
||
res = bbox ? new cNumber(bbox.c1 + 1) : null;
|
||
} else {
|
||
var arg0 = arg[0];
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
bbox = arg0.getRange();
|
||
bbox = bbox && bbox.bbox;
|
||
res = bbox ? new cNumber(bbox.c1 + 1) : null;
|
||
} else if (cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
bbox = arg0.getRange();
|
||
bbox = bbox && bbox.bbox;
|
||
|
||
if (bbox && bbox.c2 > bbox.c1) {
|
||
res = new cArray();
|
||
for (var i = bbox.c1; i <= bbox.c2; i++) {
|
||
res.addElement(new cNumber(i + 1))
|
||
}
|
||
} else {
|
||
res = bbox ? new cNumber(bbox.c1 + 1) : null;
|
||
}
|
||
}
|
||
}
|
||
return res ? res : new cError(cErrorType.bad_reference);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cCOLUMNS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cCOLUMNS.prototype = Object.create(cBaseFunction.prototype);
|
||
cCOLUMNS.prototype.constructor = cCOLUMNS;
|
||
cCOLUMNS.prototype.name = 'COLUMNS';
|
||
cCOLUMNS.prototype.argumentsMin = 1;
|
||
cCOLUMNS.prototype.argumentsMax = 1;
|
||
cCOLUMNS.prototype.arrayIndexes = {0: 1};
|
||
cCOLUMNS.prototype.argumentsType = [argType.reference];
|
||
cCOLUMNS.prototype.Calculate = function (arg) {
|
||
var arg0 = arg[0];
|
||
var range;
|
||
if (cElementType.array === arg0.type) {
|
||
return new cNumber(arg0.getCountElementInRow());
|
||
} else if (cElementType.cellsRange === arg0.type || cElementType.cell === arg0.type ||
|
||
cElementType.cell3D === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
range = arg0.getRange();
|
||
}
|
||
return (range ? new cNumber(Math.abs(range.getBBox0().c1 - range.getBBox0().c2) + 1) :
|
||
new cError(cErrorType.wrong_value_type));
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cEXPAND() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cEXPAND.prototype = Object.create(cBaseFunction.prototype);
|
||
cEXPAND.prototype.constructor = cEXPAND;
|
||
cEXPAND.prototype.name = 'EXPAND';
|
||
cEXPAND.prototype.isXLFN = true;
|
||
cEXPAND.prototype.argumentsMin = 2;
|
||
cEXPAND.prototype.argumentsMax = 4;
|
||
cEXPAND.prototype.arrayIndexes = {0: 1, 3: 1};
|
||
cEXPAND.prototype.argumentsType = [argType.reference, argType.number, argType.number, argType.any];
|
||
cEXPAND.prototype.Calculate = function (arg) {
|
||
const MAX_ARRAY_SIZE = 1048576;
|
||
let array,
|
||
arg0 = arg[0],
|
||
arg3 = arg[3] ? arg[3] : new cError(cErrorType.not_available);
|
||
|
||
function expandedArrayNew (arr, arg0Dimensions, maxRows, maxCols) {
|
||
// maxRows/cols - dimensions for resulting array
|
||
// arg0Dimensions.row/col - dimensions of existing array
|
||
let res = new cArray();
|
||
|
||
for (let i = 0; i < maxRows; i++) {
|
||
res.addRow();
|
||
for (let j = 0; j < maxCols; j++) {
|
||
if (i >= arg0Dimensions.row || j >= arg0Dimensions.col) {
|
||
res.addElement(pad_with);
|
||
continue
|
||
}
|
||
let elem = arr.getElementRowCol ? arr.getElementRowCol(i,j) : arr.getValueByRowCol(i,j);
|
||
elem ? res.addElement(elem) : res.addElement(new cEmpty());
|
||
}
|
||
}
|
||
return res;
|
||
}
|
||
|
||
// --------------------- arg0(array) type check ----------------------//
|
||
if (cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type || cElementType.array === arg0.type) {
|
||
array = arg0;
|
||
} else if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
} else if (cElementType.empty === arg0.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
array = new cArray();
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
if (arg0.getValue().type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
array.addElement(arg0.getValue());
|
||
} else {
|
||
array.addElement(arg0);
|
||
}
|
||
}
|
||
|
||
// --------------------- arg1(row) type check ----------------------//
|
||
let rows = arg[1],
|
||
arg0Dimensions = array.getDimensions();
|
||
|
||
if (cElementType.empty === rows.type) {
|
||
rows = new cNumber(arg0Dimensions.row);
|
||
} else if(cElementType.array === rows.type) {
|
||
rows = rows.getElementRowCol(0, 0);
|
||
} else if(cElementType.cellsRange === rows.type || cElementType.cellsRange3D === rows.type) {
|
||
// TODO не получилось точно выяснить поведение функции при передаче в нее cellsRange вторым или третьим аргументом, поэтому пока возвращаем ошибку
|
||
rows = new cError(cErrorType.wrong_value_type);
|
||
};
|
||
rows = rows.tocNumber();
|
||
|
||
if (cElementType.error === rows.type) {
|
||
return rows;
|
||
}
|
||
rows = rows.toNumber();
|
||
|
||
// --------------------- arg2(column) type check ----------------------//
|
||
let columns = arg[2] ? arg[2] : new cEmpty();
|
||
if(cElementType.empty === columns.type) {
|
||
columns = new cNumber(arg0Dimensions.col);
|
||
} else if(cElementType.array === columns.type) {
|
||
columns = columns.getElementRowCol(0, 0);
|
||
} else if(cElementType.cellsRange === columns.type || cElementType.cellsRange3D === columns.type) {
|
||
// TODO не получилось точно выяснить поведение функции при передаче в нее cellsRange вторым или третьим аргументом, поэтому пока возвращаем ошибку
|
||
columns = new cError(cErrorType.wrong_value_type);
|
||
}
|
||
columns = columns.tocNumber();
|
||
|
||
if(cElementType.error === columns.type) {
|
||
return columns;
|
||
}
|
||
columns = columns.toNumber();
|
||
|
||
// --------------------- arg3(pad_with) type check ----------------------//
|
||
let pad_with = arg3;
|
||
if (cElementType.cellsRange === arg3.type || cElementType.cellsRange3D === arg3.type || cElementType.array === arg3.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
// check length and max array size
|
||
if (((rows * columns) > MAX_ARRAY_SIZE)) {
|
||
return new cError(cErrorType.not_numeric);
|
||
} else if (rows < arg0Dimensions.row || columns < arg0Dimensions.col) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else if(rows >= arg0Dimensions.row && columns >= arg0Dimensions.col) {
|
||
return expandedArrayNew (array, arg0Dimensions, rows, columns);
|
||
}
|
||
|
||
return new cError(cErrorType.wrong_value_type);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cFILTER() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cFILTER.prototype = Object.create(cBaseFunction.prototype);
|
||
cFILTER.prototype.constructor = cFILTER;
|
||
cFILTER.prototype.name = 'FILTER';
|
||
cFILTER.prototype.argumentsMin = 2;
|
||
cFILTER.prototype.argumentsMax = 3;
|
||
cFILTER.prototype.isXLFN = true;
|
||
cFILTER.prototype.isXLWS = true;
|
||
cFILTER.prototype.arrayIndexes = {0: 1, 1: 1};
|
||
cFILTER.prototype.argumentsType = [argType.reference, argType.reference, argType.any];
|
||
cFILTER.prototype.Calculate = function (arg) {
|
||
function rangeModeLoop (rows, columns, isColumnMode) {
|
||
let resArr = new cArray();
|
||
|
||
for (let i = 0; i < rows; i++) {
|
||
for (let j = 0; j < columns; j++) {
|
||
let val = arg1.getValueByRowCol ? arg1.getValueByRowCol(i, j) : arg1.getElementRowCol(i, j, true);
|
||
|
||
val = val.tocBool();
|
||
val = val.toBool ? val.toBool() : new cError(cErrorType.wrong_value_type);
|
||
if (cElementType.error === val.type) {
|
||
return val;
|
||
}
|
||
|
||
if (val) {
|
||
isColumnMode ? resArr.pushCol(arg0._getCol(j), 0) : resArr.pushRow(arg0._getRow(i), 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
return resArr;
|
||
}
|
||
|
||
let resultArr = new cArray(),
|
||
arg0 = arg[0],
|
||
arg1 = arg[1],
|
||
arg2 = arg[2] ? arg[2] : new cEmpty(),
|
||
baseMode = false, // val && range || val && val || range && val
|
||
rangeMode = false; // range && range
|
||
|
||
if (cElementType.empty === arg0.type || cElementType.empty === arg1.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
if (cElementType.error === arg1.type) {
|
||
return arg1;
|
||
}
|
||
|
||
// 4 options: 1) range && range; 2) range && value; 3) value && range; 4) value && value
|
||
if ((cElementType.array === arg0.type || cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) && (cElementType.array === arg1.type || cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type)) {
|
||
// 1) range && range
|
||
rangeMode = true;
|
||
} else if ((cElementType.array === arg0.type || cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) && (cElementType.array !== arg1.type && cElementType.cellsRange !== arg1.type && cElementType.cellsRange3D !== arg1.type)) {
|
||
// 2) range && value
|
||
// Return array arg0 if arg1 === true and if array arg0 is one-dimensional
|
||
let arg0Dimensons = arg0.getDimensions();
|
||
if ((arg0Dimensons.row > 1 && arg0Dimensons.col > 1)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
baseMode = true;
|
||
}
|
||
} else if ((cElementType.array !== arg0.type && cElementType.cellsRange !== arg0.type && cElementType.cellsRange3D !== arg0.type) && (cElementType.array === arg1.type || cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type)) {
|
||
// 3) value && range
|
||
baseMode = true;
|
||
arg1 = arg1.isOneElement() ? arg1.getFirstElement() : new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
// 4) value && value
|
||
baseMode = true;
|
||
}
|
||
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
if (cElementType.error === arg1.type) {
|
||
return arg1;
|
||
}
|
||
|
||
if (rangeMode) {
|
||
const initialArrayDimensions = arg0.getDimensions(true),
|
||
initRows = initialArrayDimensions.row,
|
||
initColumns = initialArrayDimensions.col,
|
||
lookingArrayDimensions = arg1.getDimensions(true);
|
||
|
||
// check for matching array sizes
|
||
if (lookingArrayDimensions.row === 1 && lookingArrayDimensions.col === initColumns) {
|
||
resultArr = rangeModeLoop(lookingArrayDimensions.row, lookingArrayDimensions.col, true);
|
||
} else if (lookingArrayDimensions.row === initRows && lookingArrayDimensions.col === 1) {
|
||
resultArr = rangeModeLoop(lookingArrayDimensions.row, lookingArrayDimensions.col, false);
|
||
} else {
|
||
// the size of the desired array does not match the initial
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if (resultArr.type === cElementType.error) {
|
||
return resultArr;
|
||
} else {
|
||
resultArr = (resultArr.countElement > 0 || resultArr.rowCount > 0) ? resultArr : ((cElementType.empty !== arg2.type) ? arg2 : new cError(cErrorType.wrong_value_type));
|
||
}
|
||
} else if (baseMode) {
|
||
arg1 = arg1.tocBool();
|
||
arg1 = arg1.toBool ? arg1.toBool() : new cError(cErrorType.wrong_value_type);
|
||
|
||
if (cElementType.error === arg1.type) {
|
||
resultArr = arg1;
|
||
} else if (arg1) {
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
arg0 = arg0.getValue();
|
||
}
|
||
resultArr = arg0;
|
||
} else {
|
||
// should be #CALC!
|
||
resultArr = (cElementType.empty !== arg2.type) ? arg2 : new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
if (cElementType.cellsRange === resultArr.type || cElementType.cellsRange3D === resultArr.type) {
|
||
resultArr = resultArr.getFullArray();
|
||
}
|
||
|
||
return resultArr;
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cFORMULATEXT() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cFORMULATEXT.prototype = Object.create(cBaseFunction.prototype);
|
||
cFORMULATEXT.prototype.constructor = cFORMULATEXT;
|
||
cFORMULATEXT.prototype.name = 'FORMULATEXT';
|
||
cFORMULATEXT.prototype.argumentsMin = 1;
|
||
cFORMULATEXT.prototype.argumentsMax = 1;
|
||
cFORMULATEXT.prototype.isXLFN = true;
|
||
cFORMULATEXT.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.area_to_ref;
|
||
cFORMULATEXT.prototype.argumentsType = [argType.reference];
|
||
cFORMULATEXT.prototype.Calculate = function (arg) {
|
||
|
||
var arg0 = arg[0];
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
|
||
var res = null;
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type ||
|
||
cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
var bbox = arg0.getRange();
|
||
var formula = bbox.isFormula();
|
||
if (!formula) {
|
||
return new cError(cErrorType.not_available);
|
||
} else {
|
||
res = new cString(bbox.getValueForEdit(true));
|
||
}
|
||
}
|
||
|
||
return (null !== res ? res : new cError(cErrorType.wrong_value_type));
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cGETPIVOTDATA() {
|
||
}
|
||
|
||
cGETPIVOTDATA.prototype = Object.create(cBaseFunction.prototype);
|
||
cGETPIVOTDATA.prototype.constructor = cGETPIVOTDATA;
|
||
cGETPIVOTDATA.prototype.name = 'GETPIVOTDATA';
|
||
cGETPIVOTDATA.prototype.argumentsMin = 2;
|
||
cGETPIVOTDATA.prototype.argumentsMax = 254;
|
||
cGETPIVOTDATA.prototype.arrayIndexes = {0: 1, 1: 1};
|
||
cGETPIVOTDATA.prototype.argumentsType = [argType.text, argType.text, [argType.text, argType.any]];
|
||
cGETPIVOTDATA.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cGETPIVOTDATA.prototype.Calculate = function (arg) {
|
||
// arg0 - data_field - pivot table name
|
||
//The name can be entered exactly like the existing field name or only the root of the name, for example, if you enter "second" in the argument, a field named "Sum of second", "Count of second", etc. will be returned.
|
||
// arg1 - pivot_table - pivot table range
|
||
// // If none of the cells in the range touch the table, then return #REF
|
||
// ...arg2 - [field1,item1] - [field name, element] - the name and element pair point to an element in the field
|
||
|
||
|
||
const getPivotData = function (looking_field, pivot_table_ref, items_array) {
|
||
if (cElementType.cell !== pivot_table_ref.type && cElementType.cell3D !== pivot_table_ref.type && cElementType.cellsRange !== pivot_table_ref.type && cElementType.cellsRange3D !== pivot_table_ref.type) {
|
||
return refError;
|
||
}
|
||
let worksheet = pivot_table_ref.getWS();
|
||
let bbox = pivot_table_ref.getBBox0();
|
||
|
||
let pivotTables = worksheet.getPivotTablesIntersectingRange(bbox);
|
||
let pivotTable = pivotTables && pivotTables.length > 0 && pivotTables[pivotTables.length - 1];
|
||
if (pivotTable) {
|
||
let cell = pivotTable.getCellByGetPivotDataParams({
|
||
dataFieldName: looking_field,
|
||
optParams: prepareItemsArray(items_array)
|
||
});
|
||
if (cell) {
|
||
res = new cRef(worksheet.getCell3(cell.row, cell.col).getName(), worksheet);
|
||
return res.tocNumber();
|
||
}
|
||
}
|
||
return refError;
|
||
};
|
||
|
||
const getPivotDataByTwoArgs = function(pivotTableRef, stringOrCell) {
|
||
const bbox = pivotTableRef.getBBox0();
|
||
const worksheet = pivotTableRef.ws;
|
||
const pivotTables = worksheet.getPivotTablesIntersectingRange(bbox);
|
||
const pivotTable = pivotTables && pivotTables.length > 0 && pivotTables[pivotTables.length - 1];
|
||
if (pivotTable) {
|
||
return pivotTable.getCellByGetPivotDataString(stringOrCell);
|
||
}
|
||
return refError;
|
||
};
|
||
|
||
const prepareItemsArray = function (array) {
|
||
return array.map(function(elem) {
|
||
return elem.getValue();
|
||
});
|
||
};
|
||
|
||
const t = this;
|
||
let arg0 = arg[0], arg1 = arg[1], arg2 = arg.slice(2);
|
||
let refError = new cError(cErrorType.bad_reference);
|
||
let ws = arguments[3], res;
|
||
|
||
if (ws) {
|
||
if (arg.length === 2) {
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type || cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
return getPivotDataByTwoArgs(arg0, arg1);
|
||
}
|
||
}
|
||
|
||
if (cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
return refError;
|
||
}
|
||
|
||
if (cElementType.array === arg0.type) {
|
||
if (!arg0.isOneElement()) {
|
||
let resArr = new cArray();
|
||
arg0.foreach(function (elem, r, c) {
|
||
if (!resArr.array[r]) {
|
||
resArr.addRow();
|
||
}
|
||
|
||
let looking_data_field = elem.tocString();
|
||
if (cElementType.error === looking_data_field.type) {
|
||
// return arg0;
|
||
// push error in to arr and go to the next value
|
||
resArr.addElement(looking_data_field);
|
||
} else {
|
||
resArr.addElement(getPivotData(looking_data_field.getValue(), arg1, arg));
|
||
}
|
||
});
|
||
|
||
return resArr;
|
||
}
|
||
arg0 = arg0.getFirstElement();
|
||
}
|
||
|
||
arg0 = arg0.tocString();
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
|
||
res = getPivotData(arg0.getValue(), arg1, arg2);
|
||
}
|
||
|
||
if (res) {
|
||
return res
|
||
}
|
||
|
||
return refError;
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cHLOOKUP() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cHLOOKUP.prototype = Object.create(cBaseFunction.prototype);
|
||
cHLOOKUP.prototype.constructor = cHLOOKUP;
|
||
cHLOOKUP.prototype.name = 'HLOOKUP';
|
||
cHLOOKUP.prototype.argumentsMin = 3;
|
||
cHLOOKUP.prototype.argumentsMax = 4;
|
||
cHLOOKUP.prototype.arrayIndexes = {0: 1, 1: 1, 2: 1};
|
||
cHLOOKUP.prototype.argumentsType = [argType.any, argType.number, argType.number, argType.logical];
|
||
cHLOOKUP.prototype.Calculate = function (arg) {
|
||
let retArr = new cArray();
|
||
let error = false;
|
||
if (arg[0].type === cElementType.array) {
|
||
if (arg[2] && arg[2].type === cElementType.cellsRange || arg[2].type === cElementType.cellsRange3D || arg[2].type === cElementType.array) {
|
||
arg[2] = arg[2].getValue2(0,0);
|
||
}
|
||
let dimension = arg[0].getDimensions();
|
||
for (let r = 0; r < dimension.row; r++) {
|
||
retArr.addRow();
|
||
for (let c = 0; c < dimension.col; c++) {
|
||
retArr.addElement(g_oHLOOKUPCache.calculate([arg[0].getValue2(r, c), arg[1], arg[2], arg[3]], arguments[1]));
|
||
}
|
||
}
|
||
|
||
return retArr;
|
||
}
|
||
|
||
if (arg[2] && (arg[2].type === cElementType.array)) {
|
||
let dimension = arg[2].getDimensions();
|
||
for (let r = 0; r < dimension.row; r++) {
|
||
retArr.addRow();
|
||
for (let c = 0; c < dimension.col; c++) {
|
||
if (!error) {
|
||
let res = g_oHLOOKUPCache.calculate([arg[0], arg[1], arg[2].getValue2(r, c), arg[3]], arguments[1]);
|
||
if (res.type === cElementType.error) {
|
||
error = true
|
||
}
|
||
retArr.addElement(res);
|
||
} else {
|
||
break
|
||
}
|
||
}
|
||
}
|
||
return error ? new cError(cErrorType.bad_reference) : retArr;
|
||
}
|
||
|
||
return g_oHLOOKUPCache.calculate(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cHYPERLINK() {
|
||
}
|
||
|
||
cHYPERLINK.prototype = Object.create(cBaseFunction.prototype);
|
||
cHYPERLINK.prototype.constructor = cHYPERLINK;
|
||
cHYPERLINK.prototype.name = 'HYPERLINK';
|
||
cHYPERLINK.prototype.argumentsMin = 1;
|
||
cHYPERLINK.prototype.argumentsMax = 2;
|
||
cHYPERLINK.prototype.argumentsType = [argType.text, argType.any];
|
||
cHYPERLINK.prototype.Calculate = function (arg) {
|
||
var arg0 = arg[0], arg1 = arg.length === 1 ? null : arg[1];
|
||
|
||
if (arg0 instanceof cArea || arg0 instanceof cArea3D) {
|
||
arg0 = arg0.cross(arguments[1]);
|
||
} else if (arg0 instanceof cArray) {
|
||
arg0 = arg0.getElementRowCol(0, 0);
|
||
}
|
||
arg0 = arg0.tocString();
|
||
|
||
|
||
if(arg1) {
|
||
if (arg1 instanceof cArea || arg1 instanceof cArea3D) {
|
||
arg1 = arg1.cross(arguments[1]);
|
||
} else if (arg1 instanceof cArray) {
|
||
arg1 = arg1.getElementRowCol(0, 0);
|
||
}
|
||
|
||
if(arg1 instanceof cRef || arg1 instanceof cRef3D) {
|
||
arg1 = arg1.getValue();
|
||
}
|
||
if(arg1 instanceof cEmpty) {
|
||
arg1 = new cNumber(0);
|
||
}
|
||
} else {
|
||
arg1 = arg0.tocString();
|
||
}
|
||
|
||
if (arg0 instanceof cError) {
|
||
arg0.hyperlink = "";
|
||
return arg0;
|
||
}
|
||
if (arg1 instanceof cError) {
|
||
arg1.hyperlink = "";
|
||
return arg1;
|
||
}
|
||
|
||
var res = arg1;
|
||
res.hyperlink = arg0.getValue();
|
||
|
||
return res;
|
||
};
|
||
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cINDEX() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cINDEX.prototype = Object.create(cBaseFunction.prototype);
|
||
cINDEX.prototype.constructor = cINDEX;
|
||
cINDEX.prototype.name = 'INDEX';
|
||
cINDEX.prototype.argumentsMin = 2;
|
||
cINDEX.prototype.argumentsMax = 4;
|
||
cINDEX.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cINDEX.prototype.arrayIndexes = {0: 1};
|
||
cINDEX.prototype.argumentsType = [argType.reference, argType.number, argType.number];
|
||
cINDEX.prototype.Calculate = function (arg) {
|
||
let arg0 = arg[0], arg1 = arg[1] && (cElementType.empty !== arg[1].type) ? arg[1] : new cNumber(0),
|
||
arg2 = arg[2] && (cElementType.empty !== arg[2].type) ? arg[2] : new cNumber(0),
|
||
arg3 = arg[3] && (cElementType.empty !== arg[3].type) ? arg[3] : new cNumber(1), res;
|
||
|
||
if (cElementType.cellsRange3D === arg0.type) {
|
||
arg0 = arg0.tocArea();
|
||
if (!arg0) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
} else if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
|
||
let argError;
|
||
if (argError = this._checkErrorArg([arg0, arg1, arg2, arg3])) {
|
||
return argError;
|
||
}
|
||
|
||
arg1 = arg1.tocNumber();
|
||
arg2 = arg2.tocNumber();
|
||
arg3 = arg3.tocNumber();
|
||
|
||
|
||
if (cElementType.error === arg1.type || cElementType.error === arg2.type || cElementType.error === arg3.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
//TODO в дальнейшем необходимо продумать преобразования аргументов на основе argumentsType!!!
|
||
if (cElementType.array === arg1.type) {
|
||
arg1 = arg1.getElementRowCol(0,0);
|
||
if (cElementType.error === arg1.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
if (cElementType.array === arg2.type) {
|
||
arg2 = arg2.getElementRowCol(0,0);
|
||
if (cElementType.error === arg2.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
if(arg[3] && cElementType.empty !== arg[3].type && arg3 > 1) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
arg1 = arg1.getValue();
|
||
arg2 = arg2.getValue();
|
||
|
||
if (arg1 < 0 || arg2 < 0) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
const generateArray = function (_from, row, col) {
|
||
let ret = null;
|
||
let _colCount = _from.getCountElementInRow();
|
||
let _rowCount = _from.rowCount;
|
||
let i;
|
||
row = row !== undefined ? Math.ceil(row) : row;
|
||
col = col !== undefined ? Math.ceil(col) : col;
|
||
if (undefined !== row) {
|
||
if (_rowCount < row) {
|
||
if (col === undefined && _rowCount === 1 && _from.array[0] && _from.array[0][row - 1]) {
|
||
ret = new cArray();
|
||
ret.addElement(_from.array[0][row - 1]);
|
||
return ret;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
ret = new cArray();
|
||
for (i = 0; i < _colCount; i++) {
|
||
ret.addElement(_from.array[row - 1][i])
|
||
}
|
||
} else if (undefined !== col) {
|
||
if (_colCount < col) {
|
||
if (row === undefined && _colCount === 1 && _from.array[col - 1] && _from.array[col - 1][0]) {
|
||
ret = new cArray();
|
||
ret.addElement(_from.array[col - 1][0]);
|
||
return ret;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
ret = new cArray();
|
||
for (i = 0; i < _rowCount; i++) {
|
||
ret.addRow();
|
||
ret.addElement(_from.array[i][col - 1])
|
||
}
|
||
}
|
||
return ret;
|
||
};
|
||
|
||
AscCommonExcel.executeInR1C1Mode(false, function () {
|
||
let dimension = arg0.getDimensions();
|
||
let isSingleRowCol = (dimension.row > 1 && dimension.col > 1) ? false : true;
|
||
let isByColumn = (dimension.row > 1) ? true : false;
|
||
let isArray = cElementType.array === arg0.type;
|
||
|
||
let diffArg1 = arg1 === 0 ? 0 : 1;
|
||
let diffArg2 = arg2 === 0 ? 0 : 1;
|
||
|
||
if (arg[2] !== undefined && (arg1 > dimension.row || arg2 > dimension.col)) {
|
||
/* if the col_num and row_num in the arguments is greater than the array size, return an error */
|
||
res = new cError(cErrorType.bad_reference);
|
||
} else if (!isArray && arg[2] === undefined && !isSingleRowCol) {
|
||
/* if the second arg is ommited and range(exactly reference) is two dimensional, return an error */
|
||
res = new cError(cErrorType.bad_reference);
|
||
} else if (cElementType.array === arg0.type || cElementType.cellsRange === arg0.type) {
|
||
let ws = arg0.getWS ? arg0.getWS() : null, bbox = arg0.getBBox0 ? arg0.getBBox0() : null;
|
||
|
||
if (!isSingleRowCol) {
|
||
/* r&c > 1 */
|
||
if (arg1 === 0 && arg2 === 0) {
|
||
res = arg0;
|
||
} else if (arg1 === 0) {
|
||
// return full column
|
||
if (isArray) {
|
||
res = generateArray(arg0, undefined, arg2);
|
||
} else {
|
||
res = new Asc.Range(bbox.c1 + arg2 - diffArg2, bbox.r1, bbox.c1 + arg2 - diffArg2, bbox.r2);
|
||
res = new cArea(res.getName(), ws);
|
||
}
|
||
} else if (arg2 === 0) {
|
||
// return full row
|
||
if (isArray) {
|
||
res = generateArray(arg0, arg1, undefined);
|
||
} else {
|
||
res = new Asc.Range(bbox.c1, bbox.r1 + arg1 - diffArg1, bbox.c2, bbox.r1 + arg1 - diffArg1);
|
||
res = new cArea(res.getName(), ws);
|
||
}
|
||
} else {
|
||
if (isArray) {
|
||
res = arg0.getValue2(arg1 > 0 ? arg1 - 1 : 0, arg2 > 0 ? arg2 - 1 : 0);
|
||
} else {
|
||
res = new Asc.Range(bbox.c1 + arg2 - diffArg2, bbox.r1 + arg1 - diffArg1, bbox.c1 + arg2 - diffArg2, bbox.r1 + arg1 - diffArg1);
|
||
res = new cRef(res.getName(), ws);
|
||
}
|
||
}
|
||
} else {
|
||
/* r|c === 1 */
|
||
if (arg[2] === undefined && ((isByColumn && arg1 > dimension.row) || (!isByColumn && arg1 > dimension.col))) {
|
||
res = new cError(cErrorType.bad_reference);
|
||
} else if (arg1 === 0 && isByColumn) {
|
||
// res = isByColumn ? arg0 : arg0.getValue2(1, arg2);
|
||
res = arg0;
|
||
} else if (undefined !== arg[2] && arg2 === 0 && !isByColumn) {
|
||
res = arg0;
|
||
} else if (undefined === arg[2] && dimension.row === 1) {
|
||
// if the last argument is omitted and 1 line is selected
|
||
if (isArray) {
|
||
res = arg0.getValue2(0, arg1 > 0 ? arg1 - 1 : 0);
|
||
} else {
|
||
res = new Asc.Range(bbox.c1 + arg1 - diffArg1, bbox.r1, bbox.c1 + arg1 - diffArg1, bbox.r1);
|
||
res = new cRef(res.getName(), ws);
|
||
}
|
||
} else {
|
||
if (isArray) {
|
||
res = arg0.getValue2(arg1 > 0 ? arg1 - 1 : 0, arg2 > 0 ? arg2 - 1 : 0);
|
||
} else {
|
||
res = new Asc.Range(bbox.c1 + arg2 - diffArg2, bbox.r1 + arg1 - diffArg1, bbox.c1 + arg2 - diffArg2, bbox.r1 + arg1 - diffArg1);
|
||
res = new cRef(res.getName(), ws);
|
||
}
|
||
}
|
||
}
|
||
} else if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
if ((0 === arg1 || 1 === arg1) && (0 === arg2 || 1 === arg2)) {
|
||
res = arg0.getValue();
|
||
}
|
||
} else {
|
||
res = new cError(cErrorType.wrong_value_type);
|
||
}
|
||
});
|
||
|
||
return res ? res : new cError(cErrorType.bad_reference);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cINDIRECT() {
|
||
}
|
||
|
||
//TODO есть разница с MS - в тестовом файле E6
|
||
//***array-formula***
|
||
cINDIRECT.prototype = Object.create(cBaseFunction.prototype);
|
||
cINDIRECT.prototype.constructor = cINDIRECT;
|
||
cINDIRECT.prototype.name = 'INDIRECT';
|
||
cINDIRECT.prototype.argumentsMin = 1;
|
||
cINDIRECT.prototype.argumentsMax = 2;
|
||
cINDIRECT.prototype.ca = true;
|
||
cINDIRECT.prototype.argumentsType = [argType.text, argType.logical];
|
||
cINDIRECT.prototype.Calculate = function (arg) {
|
||
let t = this, arg0 = arg[0].tocString(), arg1 = arg[1] ? arg[1] : new cBool(true), ws = arguments[3],
|
||
wb = ws.workbook, o = {
|
||
Formula: "", pCurrPos: 0
|
||
}, ref, found_operand, ret;
|
||
|
||
const _getWorksheetByName = function(name) {
|
||
if(!name) {
|
||
return null;
|
||
}
|
||
for(var i = 0; i < wb.aWorksheets.length; i++)
|
||
if(wb.aWorksheets[i].getName().toLowerCase() == name.toLowerCase()){
|
||
return wb.aWorksheets[i];
|
||
}
|
||
return null;
|
||
};
|
||
|
||
function parseReference() {
|
||
let _tableTMP;
|
||
if ((ref = parserHelp.is3DRef.call(o, o.Formula, o.pCurrPos, true))[0]) {
|
||
let wsFrom = _getWorksheetByName(ref[1]);
|
||
let wsTo = (null !== ref[2]) ? _getWorksheetByName(ref[2]) : wsFrom;
|
||
if (!(wsFrom && wsTo)) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
if (parserHelp.isArea.call(o, o.Formula, o.pCurrPos)) {
|
||
found_operand = new cArea3D(o.real_str ? o.real_str.toUpperCase() : o.operand_str.toUpperCase(), wsFrom, wsTo);
|
||
} else if (parserHelp.isRef.call(o, o.Formula, o.pCurrPos)) {
|
||
if (wsTo !== wsFrom) {
|
||
found_operand = new cArea3D(o.real_str ? o.real_str.toUpperCase() : o.operand_str.toUpperCase(), wsFrom, wsTo);
|
||
} else {
|
||
found_operand = new cRef3D(o.real_str ? o.real_str.toUpperCase() : o.operand_str.toUpperCase(), wsFrom);
|
||
}
|
||
} else if (parserHelp.isName.call(o, o.Formula, o.pCurrPos)) {
|
||
found_operand = new cName3D(o.operand_str, wsFrom);
|
||
}
|
||
} else if (parserHelp.isArea.call(o, o.Formula, o.pCurrPos)) {
|
||
found_operand = new cArea(o.real_str ? o.real_str.toUpperCase() : o.operand_str.toUpperCase(), ws);
|
||
} else if (parserHelp.isRef.call(o, o.Formula, o.pCurrPos, true)) {
|
||
found_operand = new cRef(o.real_str ? o.real_str.toUpperCase() : o.operand_str.toUpperCase(), ws);
|
||
} else if (parserHelp.isName.call(o, o.Formula, o.pCurrPos)) {
|
||
found_operand = new cName(o.operand_str, ws);
|
||
} else if (_tableTMP = parserHelp.isTable.call(o, o.Formula, o.pCurrPos)) {
|
||
found_operand = AscCommonExcel.cStrucTable.prototype.createFromVal(_tableTMP, wb, ws);
|
||
|
||
if (found_operand.type === cElementType.error) {
|
||
found_operand = null;
|
||
} else {
|
||
found_operand = found_operand.toRef ? found_operand.toRef() : null;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (cElementType.array === arg0.type) {
|
||
ret = new cArray();
|
||
arg0.foreach(function (elem, r) {
|
||
o = {Formula: elem.toString(), pCurrPos: 0};
|
||
AscCommonExcel.executeInR1C1Mode(!!(arg1 && arg1.value === false), parseReference);
|
||
if (!ret.array[r]) {
|
||
ret.addRow();
|
||
}
|
||
ret.addElement(found_operand)
|
||
});
|
||
return ret;
|
||
} else {
|
||
o.Formula = arg0.toString();
|
||
AscCommonExcel.executeInR1C1Mode(!!(arg1 && arg1.value == false), parseReference);
|
||
if (found_operand) {
|
||
if (cElementType.name === found_operand.type || cElementType.name3D === found_operand.type) {
|
||
found_operand = found_operand.toRef(arguments[1]);
|
||
if (found_operand && cElementType.error === found_operand.type) {
|
||
ret = new cError(cErrorType.bad_reference);
|
||
} else {
|
||
ret = found_operand;
|
||
}
|
||
} else {
|
||
ret = found_operand;
|
||
}
|
||
} else {
|
||
ret = new cError(cErrorType.bad_reference);
|
||
}
|
||
}
|
||
// Save result for recursion check
|
||
AscCommonExcel.g_cCalcRecursion.saveFunctionResult(this.name, ret);
|
||
|
||
return ret;
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cLOOKUP() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cLOOKUP.prototype = Object.create(cBaseFunction.prototype);
|
||
cLOOKUP.prototype.constructor = cLOOKUP;
|
||
cLOOKUP.prototype.name = 'LOOKUP';
|
||
cLOOKUP.prototype.argumentsMin = 2;
|
||
cLOOKUP.prototype.argumentsMax = 3;
|
||
cLOOKUP.prototype.arrayIndexes = {0: 1, 1: 1, 2: 1};
|
||
cLOOKUP.prototype.argumentsType = [argType.any, argType.reference, argType.reference];
|
||
cLOOKUP.prototype.Calculate = function (arg) {
|
||
|
||
if (!AscCommonExcel.bIsSupportDynamicArrays && arg[0].type === cElementType.cellsRange || arg[0].type === cElementType.cellsRange3D) {
|
||
arg[0] = arg[0].isOneElement() ? arg[0].getFirstElement() : arg[0].cross(arguments[1]);
|
||
} else if (arg[0].type === cElementType.array) {
|
||
let retArr = new cArray();
|
||
let dimension = arg[0].getDimensions();
|
||
|
||
for (let r = 0; r < dimension.row; r++) {
|
||
retArr.addRow();
|
||
for (let c = 0; c < dimension.col; c++) {
|
||
retArr.addElement(g_oLOOKUPCache.calculate([arg[0].getValue2(r, c), arg[1], arg[2]], arguments[1]));
|
||
}
|
||
}
|
||
return retArr;
|
||
}
|
||
|
||
|
||
return g_oLOOKUPCache.calculate(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cMATCH() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cMATCH.prototype = Object.create(cBaseFunction.prototype);
|
||
cMATCH.prototype.constructor = cMATCH;
|
||
cMATCH.prototype.name = 'MATCH';
|
||
cMATCH.prototype.argumentsMin = 2;
|
||
cMATCH.prototype.argumentsMax = 3;
|
||
cMATCH.prototype.arrayIndexes = {1: 1};
|
||
cMATCH.prototype.argumentsType = [argType.any, argType.number, argType.number];
|
||
cMATCH.prototype.Calculate = function (arg) {
|
||
return g_oMatchCache.calculate(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cOFFSET() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cOFFSET.prototype = Object.create(cBaseFunction.prototype);
|
||
cOFFSET.prototype.constructor = cOFFSET;
|
||
cOFFSET.prototype.name = 'OFFSET';
|
||
cOFFSET.prototype.argumentsMin = 3;
|
||
cOFFSET.prototype.argumentsMax = 5;
|
||
cOFFSET.prototype.ca = true;
|
||
cOFFSET.prototype.arrayIndexes = {0: 1};
|
||
cOFFSET.prototype.argumentsType = [argType.reference, argType.number, argType.number, argType.number, argType.number];
|
||
cOFFSET.prototype.Calculate = function (arg) {
|
||
|
||
function validBBOX(bbox) {
|
||
return 0 <= bbox.r1 && bbox.r1 <= gc_nMaxRow0 && 0 <= bbox.c1 && bbox.c1 <= gc_nMaxCol0 && 0 <= bbox.r2 &&
|
||
bbox.r2 <= gc_nMaxRow0 && 0 <= bbox.c2 && bbox.c2 <= gc_nMaxCol0;
|
||
}
|
||
|
||
var arg0 = arg[0], arg1 = arg[1].tocNumber(), arg2 = arg[2].tocNumber();
|
||
var arg3 = 3 < arg.length ? (cElementType.empty === arg[3].type ? new cNumber(1) : arg[3].tocNumber()) : new cNumber(-1);
|
||
var arg4 = 4 < arg.length ? (cElementType.empty === arg[4].type ? new cNumber(1) : arg[4].tocNumber()) : new cNumber(-1);
|
||
|
||
var argError;
|
||
if (argError = this._checkErrorArg([arg0, arg1, arg2, arg3, arg4])) {
|
||
return argError;
|
||
}
|
||
|
||
arg1 = arg1.getValue();
|
||
arg2 = arg2.getValue();
|
||
arg3 = arg3.getValue();
|
||
arg4 = arg4.getValue();
|
||
|
||
if (arg3 == 0 || arg4 == 0) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
var res;
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type ||
|
||
cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
var box = arg0.getBBox0();
|
||
if (box) {
|
||
box = box.clone(true);
|
||
|
||
//в документации написано, что в отрицательных значений в 4 и 5 аргументах быть не может
|
||
//но на деле ms рассчитывает такие формулы
|
||
//сделал аналогично
|
||
|
||
box.c1 = box.c1 + arg2;
|
||
box.r1 = box.r1 + arg1;
|
||
box.c2 = box.c2 + arg2;
|
||
box.r2 = box.r2 + arg1;
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
if (arg.length > 3) {
|
||
if (arg4 < 0) {
|
||
box.c1 = box.c1 + arg4 + 1;
|
||
} else {
|
||
box.c2 = box.c1 + arg4 - 1;
|
||
}
|
||
|
||
if (arg3 < 0) {
|
||
box.r1 = box.r1 + arg3 + 1;
|
||
} else {
|
||
box.r2 = box.r1 + arg3 - 1;
|
||
}
|
||
}
|
||
} else {
|
||
if (arg.length > 3) {
|
||
if (arg4 < 0) {
|
||
box.c1 = box.c1 + arg4 + 1;
|
||
box.c2 = box.c1 - arg4 - 1;
|
||
} else {
|
||
box.c2 = box.c1 + arg4 - 1;
|
||
}
|
||
|
||
if (arg3 < 0) {
|
||
box.r1 = box.r1 + arg3 + 1;
|
||
box.r2 = box.r1 - arg3 - 1;
|
||
} else {
|
||
box.r2 = box.r1 + arg3 - 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!validBBOX(box)) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
var name;
|
||
AscCommonExcel.executeInR1C1Mode(false, function () {
|
||
name = box.getName();
|
||
});
|
||
var ws = arg0.getWS();
|
||
var wsCell = arguments[3];
|
||
if (box.isOneCell()) {
|
||
res = wsCell === ws ? new cRef(name, ws) : new cRef3D(name, ws);
|
||
} else {
|
||
res = wsCell === ws ? new cArea(name, ws) : new cArea3D(name, ws, ws);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!res) {
|
||
res = new cError(cErrorType.wrong_value_type);
|
||
}
|
||
// Save result for recursion check
|
||
AscCommonExcel.g_cCalcRecursion.saveFunctionResult(this.name, res);
|
||
|
||
return res;
|
||
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cROW() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cROW.prototype = Object.create(cBaseFunction.prototype);
|
||
cROW.prototype.constructor = cROW;
|
||
cROW.prototype.name = 'ROW';
|
||
cROW.prototype.argumentsMax = 1;
|
||
cROW.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.setArrayRefAsArg;
|
||
cROW.prototype.argumentsType = [argType.reference];
|
||
cROW.prototype.Calculate = function (arg) {
|
||
var bbox;
|
||
var res;
|
||
var opt_row = arguments[5];
|
||
if (opt_row !== undefined) {
|
||
return new cNumber(opt_row + 1);
|
||
} else if (0 === arg.length) {
|
||
bbox = arguments[1];
|
||
res = bbox ? new cNumber(bbox.r1 + 1) : null;
|
||
} else {
|
||
var arg0 = arg[0];
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
bbox = arg0.getRange();
|
||
bbox = bbox && bbox.bbox;
|
||
res = bbox ? new cNumber(bbox.r1 + 1) : null;
|
||
} else if (cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
bbox = arg0.getRange();
|
||
bbox = bbox && bbox.bbox;
|
||
|
||
if (bbox && bbox.r2 > bbox.r1) {
|
||
res = new cArray();
|
||
for (var i = bbox.r1; i <= bbox.r2; i++) {
|
||
res.addRow();
|
||
res.addElement(new cNumber(i + 1))
|
||
}
|
||
} else {
|
||
res = bbox ? new cNumber(bbox.r1 + 1) : null;
|
||
}
|
||
}
|
||
}
|
||
|
||
return res ? res : new cError(cErrorType.bad_reference);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cROWS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cROWS.prototype = Object.create(cBaseFunction.prototype);
|
||
cROWS.prototype.constructor = cROWS;
|
||
cROWS.prototype.name = 'ROWS';
|
||
cROWS.prototype.argumentsMin = 1;
|
||
cROWS.prototype.argumentsMax = 1;
|
||
cROWS.prototype.arrayIndexes = {0: 1};
|
||
cROWS.prototype.argumentsType = [argType.reference];
|
||
cROWS.prototype.Calculate = function (arg) {
|
||
var arg0 = arg[0];
|
||
var range;
|
||
if (cElementType.array === arg0.type) {
|
||
return new cNumber(arg0.getRowCount());
|
||
} else if (cElementType.cellsRange === arg0.type || cElementType.cell === arg0.type ||
|
||
cElementType.cell3D === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
range = arg0.getRange();
|
||
}
|
||
return (range ? new cNumber(Math.abs(range.getBBox0().r1 - range.getBBox0().r2) + 1) :
|
||
new cError(cErrorType.wrong_value_type));
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cRTD() {
|
||
}
|
||
|
||
cRTD.prototype = Object.create(cBaseFunction.prototype);
|
||
cRTD.prototype.constructor = cRTD;
|
||
cRTD.prototype.name = 'RTD';
|
||
cRTD.prototype.argumentsType = [argType.text, argType.text, [argType.text]];
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cSORT() {
|
||
}
|
||
|
||
cSORT.prototype = Object.create(cBaseFunction.prototype);
|
||
cSORT.prototype.constructor = cSORT;
|
||
cSORT.prototype.name = 'SORT';
|
||
cSORT.prototype.argumentsMin = 1;
|
||
cSORT.prototype.argumentsMax = 4;
|
||
cSORT.prototype.isXLFN = true;
|
||
cSORT.prototype.isXLWS = true;
|
||
cSORT.prototype.arrayIndexes = {0: 1, 1: 1, 2: 1, 3: 1};
|
||
cSORT.prototype.argumentsType = [argType.reference, argType.number, argType.number, argType.logical];
|
||
cSORT.prototype.Calculate = function (arg) {
|
||
|
||
function arrayHelper (byColArray) {
|
||
let dimensions = byColArray.getDimensions(),
|
||
resArr = new cArray(),
|
||
errVal = new cError(cErrorType.wrong_value_type),
|
||
byColVal, isFirstValRecieved = false, isSecondValRecieved = false;
|
||
|
||
// find the "truthy" values
|
||
for (let i = 0; i < dimensions.row; i++) {
|
||
resArr.addRow();
|
||
for (let j = 0; j < dimensions.col; j++) {
|
||
let elem = byColArray.getValueByRowCol ? byColArray.getValueByRowCol(i, j) : byColArray.getElementRowCol(i, j);
|
||
if (!elem) {
|
||
elem = new cEmpty();
|
||
}
|
||
|
||
if (cElementType.bool === elem.type || cElementType.number === elem.type || cElementType.empty === elem.type) {
|
||
byColVal = elem.tocBool();
|
||
} else {
|
||
elem.type === cElementType.error ? resArr.addElement(elem) : resArr.addElement(errVal);
|
||
continue;
|
||
}
|
||
|
||
if (cElementType.error === byColVal.type || (isFirstValRecieved && isSecondValRecieved)) {
|
||
resArr.addElement(errVal);
|
||
} else if (!isFirstValRecieved) {
|
||
let fValue = sortArray(array, null, sort_order, byColVal.toBool(), sort_index).getFirstElement();
|
||
resArr.addElement(fValue);
|
||
isFirstValRecieved = true;
|
||
} else if (!isSecondValRecieved) {
|
||
let sValue = new cNumber(0);
|
||
resArr.addElement(sValue);
|
||
isSecondValRecieved = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return resArr;
|
||
}
|
||
|
||
function isValidArray (array, maxRowCol) {
|
||
let dimensions = array.getDimensions();
|
||
for (let i = 0; i < dimensions.row; i++) {
|
||
for (let j = 0; j < dimensions.col; j++) {
|
||
let elem = array.getValueByRowCol ? array.getValueByRowCol(i, j) : array.getElementRowCol(i, j);
|
||
if (!elem) {
|
||
return false;
|
||
}
|
||
elem = elem.tocNumber();
|
||
if (elem.type === cElementType.error) {
|
||
return false;
|
||
} else if (Math.floor(elem.getValue()) > maxRowCol || Math.floor(elem.getValue()) <= 0) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
let arg0 = arg[0], // array
|
||
arg1 = arg[1] ? arg[1] : new cNumber(1), // sort_index
|
||
arg2 = arg[2] ? arg[2] : new cNumber(1), // sort_order
|
||
arg3 = arg[3] ? arg[3] : new cBool(false); // by_col ?
|
||
|
||
// check args err
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
} else if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
if (cElementType.error === arg0.getValue().type) {
|
||
return arg0;
|
||
}
|
||
}
|
||
if (cElementType.error === arg1.type) {
|
||
return arg1;
|
||
} else if (cElementType.cell === arg1.type || cElementType.cell3D === arg1.type) {
|
||
if (cElementType.error === arg1.getValue().type) {
|
||
return arg1;
|
||
}
|
||
}
|
||
if (cElementType.error === arg2.type) {
|
||
return arg2;
|
||
} else if (cElementType.cell === arg2.type || cElementType.cell3D === arg2.type) {
|
||
if (cElementType.error === arg2.getValue().type) {
|
||
return arg2;
|
||
}
|
||
}
|
||
if (cElementType.error === arg3.type) {
|
||
return arg3;
|
||
} else if (cElementType.cell === arg3.type || cElementType.cell3D === arg3.type) {
|
||
if (cElementType.error === arg3.getValue().type) {
|
||
return arg3;
|
||
}
|
||
}
|
||
|
||
// check args empty
|
||
if (cElementType.empty === arg1.type) {
|
||
arg1 = new cNumber(1);
|
||
}
|
||
if (cElementType.empty === arg2.type) {
|
||
arg2 = new cNumber(1);
|
||
}
|
||
if (cElementType.empty === arg3.type) {
|
||
arg3 = new cBool(false);
|
||
}
|
||
|
||
let array, sort_index, sort_order, by_col, isArg1Array = false, isArg3Array = false, maxRows, maxCols;
|
||
|
||
// check args type:
|
||
// arg0(initial array) check
|
||
if (cElementType.array !== arg0.type && cElementType.cellsRange !== arg0.type && cElementType.cellsRange3D !== arg0.type) {
|
||
let elem;
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
elem = arg0.getValue();
|
||
} else {
|
||
elem = arg0;
|
||
}
|
||
array = new cArray();
|
||
array.addElement(elem);
|
||
} else {
|
||
array = arg0;
|
||
}
|
||
|
||
maxRows = array.getDimensions().row;
|
||
maxCols = array.getDimensions().col;
|
||
|
||
// arg1(sort_index) check
|
||
if (cElementType.array !== arg1.type && cElementType.cellsRange !== arg1.type && cElementType.cellsRange3D !== arg1.type) {
|
||
sort_index = arg1.tocNumber();
|
||
} else {
|
||
isArg1Array = true;
|
||
let arg1Dimensions = arg1.getDimensions();
|
||
if (arg1Dimensions.row > maxRows || arg1Dimensions.col > maxCols) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
let firstElement = arg1.getFirstElement();
|
||
if (!firstElement) {
|
||
firstElement = new cEmpty();
|
||
}
|
||
sort_index = firstElement.tocNumber();
|
||
}
|
||
}
|
||
|
||
// arg2(sort_order) check
|
||
if (cElementType.array !== arg2.type && cElementType.cellsRange !== arg2.type && cElementType.cellsRange3D !== arg2.type) {
|
||
sort_order = arg2.tocNumber();
|
||
} else if (arg2.isOneElement()) {
|
||
sort_order = arg2.getFirstElement();
|
||
} else {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
// arg3(by_col) check
|
||
if (cElementType.array !== arg3.type && cElementType.cellsRange !== arg3.type && cElementType.cellsRange3D !== arg3.type) {
|
||
by_col = arg3.tocBool();
|
||
} else {
|
||
if (!by_col) {
|
||
by_col = new cBool(false);
|
||
}
|
||
isArg3Array = true;
|
||
}
|
||
|
||
if (cElementType.error === sort_index.type) {
|
||
return sort_index;
|
||
} else {
|
||
sort_index = Math.floor(sort_index.getValue());
|
||
}
|
||
|
||
if (cElementType.error === sort_order.type) {
|
||
return sort_order;
|
||
} else {
|
||
sort_order = Math.floor(sort_order.getValue());
|
||
}
|
||
|
||
if (cElementType.error === by_col.type) {
|
||
return by_col;
|
||
} else if (!isArg3Array && cElementType.bool !== by_col.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else if (!isArg3Array) {
|
||
by_col = by_col.toBool();
|
||
}
|
||
|
||
if (sort_index <= 0 || (sort_order !== -1 && sort_order !== 1)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if (!by_col) {
|
||
if ((sort_index > maxCols) || (isArg1Array && !isValidArray(arg1, maxCols))) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
} else {
|
||
if ((sort_index > maxRows) || (isArg1Array && !isValidArray(arg1, maxRows))) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
if (isArg3Array) {
|
||
// TODO it is not completely clear how the function works when receiving an array as the last argument
|
||
return arrayHelper(arg3);
|
||
}
|
||
|
||
return sortArray(array, null, sort_order, by_col, sort_index);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cSORTBY() {
|
||
}
|
||
|
||
cSORTBY.prototype = Object.create(cBaseFunction.prototype);
|
||
cSORTBY.prototype.constructor = cSORTBY;
|
||
cSORTBY.prototype.name = 'SORTBY';
|
||
cSORTBY.prototype.argumentsMin = 2;
|
||
cSORTBY.prototype.isXLFN = true;
|
||
// TODO infinite arrayIndexes for even/odd arguments
|
||
cSORTBY.prototype.arrayIndexes = {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1};
|
||
cSORTBY.prototype.getArrayIndex = function (index) {
|
||
return 1;
|
||
};
|
||
cSORTBY.prototype.argumentsType = [argType.array, argType.array, argType.number, [argType.array, argType.number]];
|
||
cSORTBY.prototype.Calculate = function (arg) {
|
||
function arrayHelper (arr, args) {
|
||
// Helper logic:
|
||
// We are looking for the maximum size of the array, which will later become the result
|
||
// Create the resulting array after 2 cycles (i < row, j < col)
|
||
// When creating each element, iterate through all elements with the same row col value and return the value according to the condition
|
||
// If the "correct" element has already been written at least once (true/false flag), then in this case we return 0 or #VALUE! ?
|
||
let resArr = new cArray(), resCol = 1, resRow = 1,
|
||
sort_order1, by_array1, sortOrderArr = [], isByCol, isFirstElemReceived;
|
||
|
||
// get max row & col
|
||
for (let i = 1; i < args.length; i += 2) {
|
||
let by_array = args[i],
|
||
sortOrder = args[i+1];
|
||
|
||
by_array1 = i === 1 ? by_array : by_array1;
|
||
// TODO can be array with single item and can be just single item
|
||
if (sortOrder.type === cElementType.array || sortOrder.type === cElementType.cellsRange || sortOrder.type === cElementType.cellsRange3D) {
|
||
// if single element in array, fill array with it element
|
||
let resDimensoins = sortOrder.getDimensions();
|
||
if (resRow < resDimensoins.row) {
|
||
resRow = resDimensoins.row;
|
||
}
|
||
if (resCol < resDimensoins.col) {
|
||
resCol = resDimensoins.col;
|
||
}
|
||
} else {
|
||
// create array with single element and fill it
|
||
let resArr = new cArray();
|
||
for (let i = 0; i < by_array1.getDimensions().row; i++) {
|
||
resArr.addRow();
|
||
for (let k = 0; k < by_array1.getDimensions().col; k++) {
|
||
resArr.addElement(sortOrder);
|
||
}
|
||
}
|
||
sortOrder = resArr;
|
||
}
|
||
sortOrderArr.push(sortOrder);
|
||
}
|
||
// fill resArr, go through sortOrderArr
|
||
for (let i = 0; i < resRow; i++) {
|
||
resArr.addRow();
|
||
for (let j = 0; j < resCol; j++) {
|
||
let overallSortOrder;
|
||
for (let k = 0; k < sortOrderArr.length; k++) {
|
||
overallSortOrder = sortOrderArr[k].getElementRowCol ? sortOrderArr[k].getElementRowCol(i, j) : sortOrderArr[k].getValueByRowCol(i, j);
|
||
// check element
|
||
if (!overallSortOrder) {
|
||
overallSortOrder = new cError(cErrorType.not_available);
|
||
} else if (overallSortOrder.type !== cElementType.number) {
|
||
overallSortOrder = overallSortOrder.tocNumber();
|
||
}
|
||
// if any error break the cycle
|
||
if (overallSortOrder.type === cElementType.error) {
|
||
break;
|
||
}
|
||
if (overallSortOrder.type === cElementType.number) {
|
||
// matching number check
|
||
let value = Math.floor(overallSortOrder.getValue());
|
||
if (value !== -1 && value !== 1) {
|
||
overallSortOrder = new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
overallSortOrder = new cNumber(value);
|
||
}
|
||
}
|
||
|
||
sort_order1 = k === 0 ? overallSortOrder : sort_order1;
|
||
// if any error break the cycle
|
||
if (sort_order1.type === cElementType.error) {
|
||
break;
|
||
}
|
||
}
|
||
// if elem is correct, do sort and get first element from sorted array
|
||
if (overallSortOrder.type !== cElementType.error) {
|
||
let byArrDimensions = by_array1.getDimensions();
|
||
isByCol = byArrDimensions.row === 1 ? true : false;
|
||
if (isFirstElemReceived) {
|
||
if (isByCol) {
|
||
overallSortOrder = new cNumber(0);
|
||
resArr.addElement(overallSortOrder);
|
||
} else {
|
||
overallSortOrder = new cError(cErrorType.wrong_value_type);
|
||
resArr.addElement(overallSortOrder);
|
||
}
|
||
}
|
||
// TODO need more research:
|
||
// ?If single col and many rows -> return only first correct element and errors
|
||
// ?If single row and many cols -> return not only the first correct element, but also subsequent
|
||
// else if (isFirstElemReceived && args.length > 3) {
|
||
// elem = new cError(cErrorType.wrong_value_type);
|
||
// resArr.addElement(elem);
|
||
// }
|
||
else {
|
||
let firstElem = sortArray(arr, by_array1, sort_order1.getValue(), isByCol).getFirstElement();
|
||
resArr.addElement(firstElem);
|
||
isFirstElemReceived = true;
|
||
}
|
||
} else {
|
||
resArr.addElement(overallSortOrder);
|
||
}
|
||
}
|
||
}
|
||
|
||
return resArr;
|
||
}
|
||
|
||
let args = arg.slice();
|
||
let array, by_array, sort_order, maxRows, maxCols, arrayDimensions, isByCol, isSortOrderArray, isByArrayNotArray;
|
||
|
||
// check arg0
|
||
if (cElementType.error === args[0].type) {
|
||
return args[0];
|
||
} else if (cElementType.cell === args[0].type || cElementType.cell3D === args[0].type) {
|
||
if (cElementType.error === args[0].getValue().type) {
|
||
return args[0];
|
||
}
|
||
}
|
||
if (cElementType.array !== args[0].type && cElementType.cellsRange !== args[0].type && cElementType.cellsRange3D !== args[0].type) {
|
||
let elem;
|
||
if (cElementType.cell === args[0].type || cElementType.cell3D === args[0].type) {
|
||
elem = args[0].getValue();
|
||
} else {
|
||
elem = args[0];
|
||
}
|
||
array = new cArray();
|
||
array.addElement(elem);
|
||
} else if (cElementType.cellsRange === args[0].type || cElementType.cellsRange3D === args[0].type) {
|
||
array = args[0].getFullArray();
|
||
} else {
|
||
array = args[0];
|
||
}
|
||
arrayDimensions = array.getDimensions();
|
||
maxRows = arrayDimensions.row;
|
||
maxCols = arrayDimensions.col;
|
||
|
||
if (args.length < 3) {
|
||
// add default sort_by
|
||
args[2] = new cNumber(1);
|
||
}
|
||
|
||
// check args err&empty
|
||
for (let i = 1; i < args.length; i++) {
|
||
// check errors
|
||
if (cElementType.error === args[i].type) {
|
||
return args[i];
|
||
} else if (cElementType.cell === args[i].type || cElementType.cell3D === args[i].type) {
|
||
if (cElementType.error === args[i].getValue().type) {
|
||
return args[i];
|
||
}
|
||
}
|
||
|
||
// check by_array arguments
|
||
if (i % 2 !== 0) {
|
||
if (cElementType.array !== args[i].type && cElementType.cellsRange !== args[i].type && cElementType.cellsRange3D !== args[i].type) {
|
||
let elem;
|
||
if (cElementType.cell === args[i].type || cElementType.cell3D === args[i].type) {
|
||
elem = args[i].getValue();
|
||
} else {
|
||
elem = args[i];
|
||
}
|
||
|
||
by_array = new cArray();
|
||
by_array.addElement(elem);
|
||
|
||
args[i] = by_array;
|
||
isByArrayNotArray = i === 1 ? true : isByArrayNotArray;
|
||
}
|
||
}
|
||
|
||
// check sort_order arguments
|
||
if (i % 2 === 0) {
|
||
// empty check
|
||
if (cElementType.empty === args[i].type && (i % 2 === 0)) {
|
||
args[i] = new cNumber(1);
|
||
}
|
||
|
||
// variable typing
|
||
if (cElementType.array !== args[i].type && cElementType.cellsRange !== args[i].type && cElementType.cellsRange3D !== args[i].type) {
|
||
if (cElementType.cell === args[i].type || cElementType.cell3D === args[i].type) {
|
||
sort_order = args[i].getValue().tocNumber();
|
||
} else {
|
||
sort_order = args[i].tocNumber();
|
||
}
|
||
} else if (args[i].isOneElement()) {
|
||
sort_order = args[i].getFirstElement();
|
||
} else {
|
||
sort_order = args[i];
|
||
isSortOrderArray = true;
|
||
}
|
||
|
||
// check after typing and round
|
||
if (cElementType.error === sort_order.type) {
|
||
return sort_order;
|
||
} else if (!isSortOrderArray) {
|
||
sort_order = Math.floor(sort_order.getValue());
|
||
if (sort_order !== 1 && sort_order !== -1) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
sort_order = new cNumber(sort_order);
|
||
}
|
||
|
||
// check sort_order value
|
||
args[i] = sort_order;
|
||
}
|
||
}
|
||
|
||
// if the first of the by_array arguments is not an array/area - return initial array(arg0)
|
||
if (isByArrayNotArray && !isSortOrderArray) {
|
||
return array;
|
||
}
|
||
|
||
if (isSortOrderArray) {
|
||
return arrayHelper(array, args);
|
||
} else {
|
||
// dimensions check:
|
||
// check on errors first, then check on truthy dimensions and do things with it
|
||
for (let i = 1; i < args.length; i += 2) {
|
||
let byArrDimensions = args[i].getDimensions();
|
||
|
||
// TODO if there is a match on the single row, but not on the col - return the original array
|
||
if (maxRows === 1) {
|
||
// single row with elements
|
||
if (maxRows === 1 && byArrDimensions.row === 1) {
|
||
if (maxCols !== byArrDimensions.col) {
|
||
// area to array
|
||
if (cElementType.cellsRange === array.type || cElementType.cellsRange3D === array.type) {
|
||
return array.getFullArray();
|
||
}
|
||
return array;
|
||
}
|
||
// else {
|
||
// // return sorted array
|
||
// }
|
||
}
|
||
}
|
||
|
||
// isByCol or not determined by the first byarray arg
|
||
if ((byArrDimensions.row === 1 && byArrDimensions.col !== maxCols) ||
|
||
(byArrDimensions.col === 1 && byArrDimensions.row !== maxRows) ||
|
||
(byArrDimensions.col > 1 && byArrDimensions.row > 1)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else if (byArrDimensions.row === 1 && byArrDimensions.col === maxCols) {
|
||
isByCol = i === 1 ? true : isByCol;
|
||
} else if (byArrDimensions.col === 1 && byArrDimensions.row === maxRows) {
|
||
isByCol = i === 1 ? false : isByCol;
|
||
}
|
||
}
|
||
}
|
||
|
||
return sortByArrayWrapper(array, args, isByCol);
|
||
};
|
||
cSORTBY.prototype.checkArguments = function (countArguments) {
|
||
return countArguments === 2 ? true : 1 === countArguments % 2 && cBaseFunction.prototype.checkArguments.apply(this, arguments);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cTRANSPOSE() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cTRANSPOSE.prototype = Object.create(cBaseFunction.prototype);
|
||
cTRANSPOSE.prototype.constructor = cTRANSPOSE;
|
||
cTRANSPOSE.prototype.name = 'TRANSPOSE';
|
||
cTRANSPOSE.prototype.argumentsMin = 1;
|
||
cTRANSPOSE.prototype.argumentsMax = 1;
|
||
cTRANSPOSE.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cTRANSPOSE.prototype.arrayIndexes = {0: 1};
|
||
cTRANSPOSE.prototype.argumentsType = [argType.any];
|
||
cTRANSPOSE.prototype.Calculate = function (arg) {
|
||
|
||
function TransposeMatrix(A) {
|
||
|
||
var tMatrix = [], res = new cArray();
|
||
|
||
for (var i = 0; i < A.length; i++) {
|
||
for (var j = 0; j < A[i].length; j++) {
|
||
if (!tMatrix[j]) {
|
||
tMatrix[j] = [];
|
||
}
|
||
tMatrix[j][i] = A[i][j];
|
||
}
|
||
}
|
||
|
||
res.fillFromArray(tMatrix);
|
||
|
||
return res;
|
||
}
|
||
|
||
var arg0 = arg[0];
|
||
if (cElementType.cellsRange === arg0.type) {
|
||
//TODO возможно стоит на вход функции Calculate в случае применения как формулы массива сразу передавать преобразованный range в array
|
||
if(!this.bArrayFormula) {
|
||
arg0 = arg0.cross(arguments[1]);
|
||
return arg0;
|
||
} else {
|
||
arg0 = arg0.getMatrix();
|
||
}
|
||
} else if(cElementType.cellsRange3D === arg0.type) {
|
||
//TODO возможно стоит на вход функции в случае применения как формулы массива сразу передавать преобразованный range в array
|
||
arg0 = arg0.getMatrix()[0];
|
||
} else if(cElementType.array === arg0.type) {
|
||
arg0 = arg0.getMatrix();
|
||
} else if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
return arg0.getValue();
|
||
} else if (cElementType.number === arg0.type || cElementType.string === arg0.type ||
|
||
cElementType.bool === arg0.type || cElementType.error === arg0.type) {
|
||
return arg0;
|
||
} else {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
if(0 === arg0.length){
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
return TransposeMatrix(arg0);
|
||
};
|
||
|
||
function takeDrop(arg, argument1, isDrop) {
|
||
var argError = cBaseFunction.prototype._checkErrorArg.call(this, arg);
|
||
if (argError) {
|
||
return argError;
|
||
}
|
||
|
||
let arg1 = arg[0];
|
||
let matrix;
|
||
if (arg1.type === cElementType.array) {
|
||
// array.getMatrix() doesn't make an independent copy and leaves a link to the original array
|
||
matrix = arg1.getMatrixCopy();
|
||
} else if (arg1.type === cElementType.cellsRange || arg1.type === cElementType.cell || arg1.type === cElementType.cell3D) {
|
||
matrix = arg1.getMatrix();
|
||
} else if (arg1.type === cElementType.cellsRange3D) {
|
||
if (arg1.isSingleSheet()) {
|
||
matrix = arg1.getMatrix()[0];
|
||
} else {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
} else if (arg1.type === cElementType.error) {
|
||
return arg1;
|
||
} else if (arg1.type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
matrix = [[arg1]];
|
||
}
|
||
|
||
let array = new cArray();
|
||
array.fillFromArray(matrix);
|
||
|
||
let arg2 = arg[1];
|
||
if (cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type) {
|
||
//_arg = _arg.getValue2(0,0);
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else if (cElementType.array === arg2.type) {
|
||
//_arg = _arg.getElementRowCol(0, 0);
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
|
||
if (cElementType.empty === arg2.type) {
|
||
arg2 = null;
|
||
} else {
|
||
arg2 = arg2.tocNumber();
|
||
if (arg2.type === cElementType.error) {
|
||
return arg2;
|
||
}
|
||
arg2 = arg2.toNumber();
|
||
arg2 = parseInt(arg2);
|
||
if (Math.abs(arg2) < 1) {
|
||
return new cError(cErrorType.array_not_calc);
|
||
}
|
||
}
|
||
|
||
let arg3 = arg[2] ? arg[2] : new cEmpty();
|
||
if (cElementType.cellsRange === arg3.type || cElementType.cellsRange3D === arg3.type) {
|
||
//_arg = _arg.getValue2(0,0);
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else if (cElementType.array === arg3.type) {
|
||
//_arg = _arg.getElementRowCol(0, 0);
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
|
||
if (cElementType.empty === arg3.type) {
|
||
arg3 = null;
|
||
} else {
|
||
arg3 = arg3.tocNumber();
|
||
if (arg3.type === cElementType.error) {
|
||
return arg3;
|
||
}
|
||
arg3 = arg3.toNumber();
|
||
arg3 = parseInt(arg3);
|
||
if (Math.abs(arg3) < 1) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
if (isDrop) {
|
||
let dimensions = array.getDimensions();
|
||
|
||
if (arg2 && dimensions.row <= Math.abs(arg2)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
if (arg3 && dimensions.col <= Math.abs(arg3)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if (arg2) {
|
||
if (arg2 < 0) {
|
||
arg2 = dimensions.row - Math.abs(arg2);
|
||
} else {
|
||
arg2 = -1 * (dimensions.row - arg2);
|
||
}
|
||
}
|
||
if (arg3) {
|
||
if (arg3 < 0) {
|
||
arg3 = dimensions.col - Math.abs(arg3);
|
||
} else {
|
||
arg3 = -1 * (dimensions.col - arg3);
|
||
}
|
||
}
|
||
}
|
||
|
||
let res = array.crop(arg2, arg3);
|
||
return res ? res : new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cTAKE() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cTAKE.prototype = Object.create(cBaseFunction.prototype);
|
||
cTAKE.prototype.constructor = cTAKE;
|
||
cTAKE.prototype.name = 'TAKE';
|
||
cTAKE.prototype.argumentsMin = 2;
|
||
cTAKE.prototype.argumentsMax = 3;
|
||
cTAKE.prototype.arrayIndexes = {0: 1};
|
||
cTAKE.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cTAKE.prototype.isXLFN = true;
|
||
cTAKE.prototype.argumentsType = [argType.reference, argType.number, argType.number];
|
||
cTAKE.prototype.arrayIndexes = {0: 1};
|
||
cTAKE.prototype.Calculate = function (arg) {
|
||
return takeDrop(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cDROP() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cDROP.prototype = Object.create(cBaseFunction.prototype);
|
||
cDROP.prototype.constructor = cDROP;
|
||
cDROP.prototype.name = 'DROP';
|
||
cDROP.prototype.argumentsMin = 2;
|
||
cDROP.prototype.argumentsMax = 3;
|
||
cDROP.prototype.arrayIndexes = {0: 1};
|
||
cDROP.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cDROP.prototype.isXLFN = true;
|
||
cDROP.prototype.argumentsType = [argType.reference, argType.number, argType.number];
|
||
cDROP.prototype.arrayIndexes = {0: 1};
|
||
cDROP.prototype.Calculate = function (arg) {
|
||
return takeDrop(arg, arguments[1], true);
|
||
};
|
||
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cUNIQUE() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cUNIQUE.prototype = Object.create(cBaseFunction.prototype);
|
||
cUNIQUE.prototype.constructor = cUNIQUE;
|
||
cUNIQUE.prototype.name = 'UNIQUE';
|
||
cUNIQUE.prototype.argumentsMin = 1;
|
||
cUNIQUE.prototype.argumentsMax = 3;
|
||
cUNIQUE.prototype.arrayIndexes = {0: 1};
|
||
cUNIQUE.prototype.argumentsType = [argType.reference, argType.logical, argType.logical];
|
||
cUNIQUE.prototype.isXLFN = true;
|
||
cUNIQUE.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cUNIQUE.prototype.Calculate = function (arg) {
|
||
|
||
var _getUniqueArr = function (_arr, _byCol, _exactlyOnce) {
|
||
var rowCount = _arr && _arr.length;
|
||
var colCount = _arr && _arr[0] && _arr[0].length;
|
||
if (!rowCount || !colCount) {
|
||
return cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
var res = new cArray();
|
||
var repeateArr = [];
|
||
var i, j, n, _value;
|
||
var resArr = [];
|
||
|
||
var _key;
|
||
if (!_byCol) {
|
||
var _rowCount = 0;
|
||
for (i = 0; i < rowCount; i++) {
|
||
_key = "";
|
||
for (j = 0; j < colCount; j++) {
|
||
_value = _arr[i][j].getValue();
|
||
_key += _value + ";";
|
||
if (j === colCount - 1) {
|
||
if (!repeateArr[_key]) {
|
||
repeateArr[_key] = {index: _rowCount, count: 1};
|
||
for (n = 0; n < colCount; n++) {
|
||
if (!resArr[_rowCount]) {
|
||
resArr[_rowCount] = [];
|
||
}
|
||
resArr[_rowCount].push(_arr[i][n]);
|
||
}
|
||
_rowCount++;
|
||
} else {
|
||
repeateArr[_key].count++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
var _colCount = 0;
|
||
for (i = 0; i < colCount; i++) {
|
||
_key = "";
|
||
for (j = 0; j < rowCount; j++) {
|
||
_value = _arr[j][i].getValue();
|
||
_key += _value + ";";
|
||
if (j === rowCount - 1) {
|
||
if (!repeateArr[_key]) {
|
||
repeateArr[_key] = {index: _colCount, count: 1};
|
||
for (n = 0; n < rowCount; n++) {
|
||
if (!resArr[n]) {
|
||
resArr[n] = [];
|
||
}
|
||
resArr[n][_colCount] = _arr[n][i];
|
||
}
|
||
_colCount++;
|
||
} else {
|
||
repeateArr[_key].count++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (_exactlyOnce) {
|
||
var tempArr = [];
|
||
var _counter = 0;
|
||
for (i in repeateArr) {
|
||
var _elem = repeateArr[i];
|
||
if (_elem.count > 1) {
|
||
continue;
|
||
}
|
||
if (!_byCol) {
|
||
tempArr[_counter] = resArr[_elem.index];
|
||
} else {
|
||
for (j = 0; j < rowCount; j++) {
|
||
if (!tempArr[j]) {
|
||
tempArr[j] = [];
|
||
}
|
||
tempArr[j][_counter] = resArr[j][_elem.index];
|
||
}
|
||
}
|
||
_counter++;
|
||
}
|
||
|
||
resArr = tempArr;
|
||
}
|
||
|
||
if (!resArr.length) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
res.fillFromArray(resArr);
|
||
|
||
return res;
|
||
};
|
||
|
||
var arg0 = arg[0];
|
||
if (cElementType.cellsRange === arg0.type) {
|
||
arg0 = arg0.getMatrix();
|
||
} else if(cElementType.cellsRange3D === arg0.type) {
|
||
arg0 = arg0.getMatrix()[0];
|
||
} else if(cElementType.array === arg0.type) {
|
||
arg0 = arg0.getMatrix();
|
||
} else if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
return arg0.getValue();
|
||
} else if (cElementType.number === arg0.type || cElementType.string === arg0.type ||
|
||
cElementType.bool === arg0.type || cElementType.error === arg0.type) {
|
||
return arg0;
|
||
} else {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
if(0 === arg0.length){
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
var arg1 = !arg[1] ? false : arg[1].tocBool();
|
||
if (arg1 && cElementType.error === arg1.type) {
|
||
return arg1;
|
||
} else if (arg1) {
|
||
arg1 = arg1.toBool();
|
||
}
|
||
|
||
var arg2 = !arg[2] ? false : arg[2].tocBool();
|
||
if (arg2 && cElementType.error === arg2.type) {
|
||
return arg2;
|
||
} else if (arg2) {
|
||
arg2 = arg2.toBool();
|
||
}
|
||
|
||
return _getUniqueArr(arg0, arg1, arg2);
|
||
};
|
||
|
||
/**
|
||
* @typedef {cNumber | cString | cBool | cError} LookUpElement
|
||
*/
|
||
|
||
function TypedMapCache() {
|
||
this.data = {};
|
||
}
|
||
|
||
TypedMapCache.prototype.getData = function (ws, rowCol, type, startIndex, endIndex, bHor) {
|
||
const wsId = ws.getId();
|
||
if (!this.data[wsId]) {
|
||
this.data[wsId] = {vertical: {}, horizontal: {}};
|
||
}
|
||
const axisData = bHor ? this.data[wsId].horizontal : this.data[wsId].vertical;
|
||
if (!axisData[rowCol]) {
|
||
axisData[rowCol] = {start: startIndex, end: startIndex - 1, data: {}};
|
||
const c1 = bHor ? startIndex : rowCol;
|
||
const r1 = bHor ? rowCol : startIndex;
|
||
const c2 = bHor ? endIndex : rowCol;
|
||
const r2 = bHor ? rowCol : endIndex;
|
||
const fullRange = ws.getRange3(r1, c1, r2, c2);
|
||
fullRange._foreachNoEmpty(function (cell, r, c) {
|
||
const value = checkTypeCell(cell, true);
|
||
const index = bHor ? c : r;
|
||
if (index > axisData[rowCol].end) {
|
||
if (!axisData[rowCol].data[value.type]) {
|
||
axisData[rowCol].data[value.type] = new Map();
|
||
}
|
||
const map = axisData[rowCol].data[value.type];
|
||
if (!map.has(value.value)) {
|
||
map.set(value.value, []);
|
||
}
|
||
const arr = axisData[rowCol].data[value.type].get(value.value);
|
||
arr.push(index);
|
||
axisData[rowCol].end = index;
|
||
}
|
||
});
|
||
axisData[rowCol].end = endIndex;
|
||
} else {
|
||
if (startIndex < axisData[rowCol].start) {
|
||
const c1 = bHor ? startIndex : rowCol;
|
||
const r1 = bHor ? rowCol : startIndex;
|
||
const c2 = bHor ? axisData[rowCol].start - 1: rowCol;
|
||
const r2 = bHor ? rowCol : axisData[rowCol].start - 1;
|
||
const fullRange = ws.getRange3(r1, c1, r2, c2);
|
||
const unshiftMaps = {};
|
||
axisData[rowCol].start = startIndex;
|
||
fullRange._foreachNoEmpty(function (cell, r, c) {
|
||
const value = checkTypeCell(cell, true);
|
||
const index = bHor ? c : r;
|
||
if (!axisData[rowCol].data[value.type]) {
|
||
axisData[rowCol].data[value.type] = new Map();
|
||
}
|
||
const map = axisData[rowCol].data[value.type];
|
||
if (!map.has(value.value)) {
|
||
map.set(value.value, [index]);
|
||
} else {
|
||
if (!unshiftMaps[value.type]) {
|
||
unshiftMaps[value.type] = new Map();
|
||
}
|
||
const unshiftMap = unshiftMaps[value.type];
|
||
if (!unshiftMap.has(value.value)) {
|
||
unshiftMap.set(value.value, []);
|
||
}
|
||
const arr = unshiftMap.get(value.value);
|
||
arr.push(index);
|
||
}
|
||
});
|
||
for (let i in unshiftMaps) {
|
||
const unshiftMap = unshiftMaps[i];
|
||
unshiftMap.forEach(function (value, key) {
|
||
const map = axisData[rowCol].data[i];
|
||
const prevArr = map.get(key);
|
||
map.set(key, value.concat(prevArr));
|
||
})
|
||
}
|
||
}
|
||
if (endIndex > axisData[rowCol].end) {
|
||
const c1 = bHor ? axisData[rowCol].end + 1: rowCol;
|
||
const r1 = bHor ? rowCol : axisData[rowCol].end + 1;
|
||
const c2 = bHor ? endIndex : rowCol;
|
||
const r2 = bHor ? rowCol : endIndex;
|
||
const fullRange = ws.getRange3(r1, c1, r2, c2);
|
||
fullRange._foreachNoEmpty(function (cell, r, c) {
|
||
const value = checkTypeCell(cell, true);
|
||
const index = bHor ? c : r;
|
||
if (index > axisData[rowCol].end) {
|
||
if (!axisData[rowCol].data[value.type]) {
|
||
axisData[rowCol].data[value.type] = new Map();
|
||
}
|
||
const map = axisData[rowCol].data[value.type];
|
||
if (!map.has(value.value)) {
|
||
map.set(value.value, []);
|
||
}
|
||
const arr = axisData[rowCol].data[value.type].get(value.value);
|
||
arr.push(index);
|
||
axisData[rowCol].end = index;
|
||
}
|
||
});
|
||
axisData[rowCol].end = endIndex;
|
||
}
|
||
}
|
||
return axisData[rowCol].data[type];
|
||
};
|
||
TypedMapCache.prototype.changeAxisData = function (cellRowOrCol, axisData, oldValue, oldType, newValue, newType, bHor) {
|
||
const staticIndex = bHor ? cellRowOrCol.nRow : cellRowOrCol.nCol;
|
||
const changedIndex = bHor ? cellRowOrCol.nCol : cellRowOrCol.nRow;
|
||
const colRowData = axisData[staticIndex];
|
||
if (colRowData && changedIndex >= colRowData.start && changedIndex <= colRowData.end) {
|
||
if (oldValue !== null) {
|
||
const mapOldType = colRowData.data[oldType];
|
||
if (mapOldType) {
|
||
if (mapOldType.has(oldValue)) {
|
||
const arr = mapOldType.get(oldValue);
|
||
if (arr.length === 1) {
|
||
mapOldType.delete(oldValue);
|
||
} else {
|
||
const newArr = [];
|
||
for (let i = 0; i < arr.length; i += 1) {
|
||
if (arr[i] !== changedIndex) {
|
||
newArr.push(arr[i]);
|
||
}
|
||
}
|
||
mapOldType.set(oldValue, newArr);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (newValue !== null) {
|
||
const mapNewType = colRowData.data[newType];
|
||
if (mapNewType) {
|
||
if (mapNewType.has(newValue)) {
|
||
const arr = mapNewType.get(newValue);
|
||
let newArr = [];
|
||
if (arr.length === 1) {
|
||
newArr = changedIndex > arr[0] ? [arr[0], changedIndex] :[changedIndex, arr[0]];
|
||
} else {
|
||
let isAdded = false;
|
||
for (let i = 0; i < arr.length; i += 1) {
|
||
if (changedIndex < arr[i] && !isAdded) {
|
||
isAdded = true;
|
||
newArr.push(changedIndex);
|
||
}
|
||
newArr.push(arr[i]);
|
||
}
|
||
if (changedIndex > arr[arr.length - 1]) {
|
||
newArr.push(changedIndex);
|
||
}
|
||
}
|
||
mapNewType.set(newValue, newArr);
|
||
} else {
|
||
mapNewType.set(newValue, [changedIndex]);
|
||
}
|
||
} else {
|
||
colRowData.data[newType] = new Map();
|
||
colRowData.data[newType].set(newValue, [changedIndex])
|
||
}
|
||
}
|
||
}
|
||
};
|
||
TypedMapCache.prototype.changeData = function (cell, dataOld, dataNew) {
|
||
const wsId = cell.ws.getId();
|
||
const data = this.data[wsId];
|
||
if (data) {
|
||
const verticalData = data.vertical;
|
||
const horizontalData = data.horizontal;
|
||
let oldValue = null;
|
||
let oldType = null;
|
||
if (dataOld) {
|
||
const oldCellValue = dataOld && dataOld.value;
|
||
oldType = oldCellValue && oldCellValue.type;
|
||
oldValue = oldType === cElementType.number ? oldCellValue.number : oldCellValue.text;
|
||
if (oldValue && oldType === cElementType.string) {
|
||
oldValue = oldValue.toLowerCase();
|
||
}
|
||
}
|
||
let newValue = null;
|
||
let newType = null;
|
||
if (dataNew) {
|
||
newType = dataNew.type;
|
||
newValue = dataNew.value;
|
||
if (newValue && newType === cElementType.string) {
|
||
newValue = newValue.toLowerCase();
|
||
}
|
||
}
|
||
if (oldType === newType && newValue === oldValue) {
|
||
return;
|
||
}
|
||
this.changeAxisData(cell, verticalData, oldValue, oldType, newValue, newType, false);
|
||
this.changeAxisData(cell, horizontalData, oldValue, oldType, newValue, newType, true);
|
||
}
|
||
};
|
||
/**
|
||
* @constructor
|
||
*/
|
||
function VHLOOKUPCache(bHor) {
|
||
this.cacheId = {};
|
||
this.cacheRanges = {};
|
||
this.bHor = bHor;
|
||
this.sortedCache = new TypedMapCache();
|
||
this.typedCache = new TypedCache();
|
||
this.typedCacheValuesMap = new TypedCache();
|
||
}
|
||
|
||
VHLOOKUPCache.prototype.calculate = function (arg, argument1) {
|
||
let arg0 = arg[0], arg1 = arg[1], arg2 = arg[2];
|
||
let arg3 = arg[3] ? arg[3].tocBool().value : true;
|
||
let opt_xlookup = arg[4] !== undefined;
|
||
let opt_arg4, opt_arg5;
|
||
if (opt_xlookup) {
|
||
opt_arg4 = arg[4];
|
||
opt_arg5 = arg[5];
|
||
}
|
||
let t = this, number, r, c, res = -1, count;
|
||
|
||
if (!opt_xlookup) {
|
||
if (cElementType.cell3D === arg2.type || cElementType.cell === arg2.type) {
|
||
arg2 = arg2.getValue();
|
||
} else if (cElementType.array === arg2.type) {
|
||
arg2 = arg2.getElementRowCol(0, 0);
|
||
} else if (cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type) {
|
||
arg2 = arg2.cross(argument1);
|
||
}
|
||
|
||
if (cElementType.error === arg2.type) {
|
||
return arg2;
|
||
}
|
||
|
||
number = arg2.getValue() - 1;
|
||
|
||
if (isNaN(number)) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
if (number < 0) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
if (cElementType.cell3D === arg0.type || cElementType.cell === arg0.type) {
|
||
arg0 = arg0.getValue();
|
||
}
|
||
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
|
||
let arg0Val;
|
||
if (cElementType.array === arg0.type) {
|
||
arg0Val = arg0.getElementRowCol(0, 0);
|
||
} else {
|
||
if (cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
arg0Val = arg0.cross(argument1);
|
||
} else {
|
||
arg0Val = arg0;
|
||
}
|
||
}
|
||
|
||
|
||
if (cElementType.error === arg0Val.type) {
|
||
return arg0Val;
|
||
}
|
||
if (cElementType.empty === arg0Val.type) {
|
||
if (!opt_xlookup) {
|
||
arg0Val = arg0Val.tocNumber();
|
||
}
|
||
}
|
||
|
||
let arg0ValType = arg0Val.type
|
||
if (cElementType.array === arg1.type && !opt_xlookup) {
|
||
let arrayToSearch;
|
||
if (this.bHor) {
|
||
arrayToSearch = arg1.getRow(0);
|
||
} else {
|
||
arrayToSearch = arg1.getCol(0);
|
||
}
|
||
|
||
if (arrayToSearch) {
|
||
if (arg3) {
|
||
// approximate(binary) search
|
||
res = _func.lookupBinarySearch(arg0Val, arrayToSearch, false);
|
||
} else {
|
||
let searchValue = arg0Val.getValue();
|
||
if (arg0Val.type === cElementType.string) {
|
||
searchValue = searchValue.toLowerCase();
|
||
}
|
||
// exact (simple) search
|
||
for (let i = 0; i < arrayToSearch.length; i++) {
|
||
let elem = arrayToSearch[i];
|
||
if (elem.type !== arg0ValType) {
|
||
continue;
|
||
}
|
||
let elemValue = elem.getValue();
|
||
|
||
if (elem.type === cElementType.string) {
|
||
elemValue = elemValue.toLowerCase();
|
||
}
|
||
|
||
if (elemValue === searchValue) {
|
||
res = i;
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if (-1 === res) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
count = this.bHor ? arg1.getRowCount() : arg1.getCountElementInRow();
|
||
if (number > count - 1) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
r = this.bHor ? number : res;
|
||
c = this.bHor ? res : number;
|
||
return arg1.getElementRowCol(r, c);
|
||
}
|
||
|
||
let range;
|
||
if (cElementType.cell === arg1.type || cElementType.cell3D === arg1.type ||
|
||
cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type) {
|
||
range = arg1.getRange();
|
||
} else if (cElementType.array === arg1.type && opt_xlookup) {
|
||
let _cacheElem = {elements: []};
|
||
arg1.foreach(function (elem, r, c) {
|
||
if (elem && elem.type === cElementType.string) {
|
||
elem.value = elem.value.toLowerCase();
|
||
}
|
||
_cacheElem.elements.push({v: elem, i: (t.bHor ? c : r)});
|
||
});
|
||
const elems = _cacheElem.elements;
|
||
return this._calculate(arg0Val, null, opt_arg4, opt_arg5, elems, null, null, 0, elems.length - 1);
|
||
}
|
||
|
||
if (!range) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
let bb = range.getBBox0();
|
||
count = this.bHor ? (bb.r2 - bb.r1) : (bb.c2 - bb.c1);
|
||
if (number > count) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
let ws = arg1.getWS();
|
||
r = this.bHor ? bb.r1 : bb.r2;
|
||
c = this.bHor ? bb.c2 : bb.c1;
|
||
let oSearchRange = ws.getRange3(bb.r1, bb.c1, r, c);
|
||
|
||
|
||
res = this._get(oSearchRange, arg0Val, arg3, opt_arg4, opt_arg5);
|
||
if (opt_xlookup) {
|
||
return res;
|
||
}
|
||
|
||
if (-1 === res) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
r = this.bHor ? bb.r1 + number : res;
|
||
c = this.bHor ? res : bb.c1 + number;
|
||
let resVal;
|
||
arg1.getWS()._getCellNoEmpty(r, c, function (cell) {
|
||
resVal = checkTypeCell(cell);
|
||
});
|
||
if (cElementType.empty === resVal.type) {
|
||
resVal = new cNumber(0);
|
||
}
|
||
|
||
return resVal;
|
||
};
|
||
|
||
/**
|
||
* @typedef {{number: {cElementType: Uint32Array}}} TypedCacheAxis
|
||
*/
|
||
|
||
/**
|
||
* @constructor
|
||
* @property {{string: {horizontal: TypedCacheAxis, vertical: TypedCacheAxis}}} data
|
||
*/
|
||
function TypedCache() {
|
||
this.data = {};
|
||
}
|
||
|
||
/**
|
||
* @static
|
||
* @param tmpArrays
|
||
* @return {any}
|
||
*/
|
||
TypedCache.prototype.sortValues = function(tmpArrays) {
|
||
for(let i in tmpArrays) {
|
||
if (i === String(cElementType.number)) {
|
||
tmpArrays[i].sort(function (a, b) {
|
||
return a.v - b.v;
|
||
});
|
||
} else {
|
||
tmpArrays[i].sort(function (a, b) {
|
||
const valueA = a.v;
|
||
const valueB = b.v;
|
||
return AscCommonExcel.stringCompare(valueA, valueB);
|
||
});
|
||
}
|
||
}
|
||
return tmpArrays;
|
||
}
|
||
|
||
/**
|
||
* @param {Worksheet} ws
|
||
* @param {boolean} bHor
|
||
* @param {number} rowCol
|
||
* @param {cElementType} elementType
|
||
* @param {(value: LookUpElement, i: number) => any} savingValueCallback
|
||
* @param {(value: any) => number} tmpToTypedCallback
|
||
* @param {(value: {cElementType: any[]}) => void} [tmpArrayCallback]
|
||
* @return {Uint32Array}
|
||
*/
|
||
TypedCache.prototype.getCache = function(ws, bHor, rowCol, elementType, savingValueCallback, tmpToTypedCallback, tmpArrayCallback) {
|
||
const wsId = ws.Get_Id();
|
||
if (!this.data[wsId]) {
|
||
this.data[wsId] = {horizontal: {}, vertical: {}};
|
||
}
|
||
/** @type {TypedCacheAxis} */
|
||
const axisData = bHor? this.data[wsId].horizontal : this.data[wsId].vertical;
|
||
if (!axisData[rowCol]) {
|
||
let container = {};
|
||
container[rowCol] = {};
|
||
this.generateCache(ws, bHor, rowCol, savingValueCallback, tmpToTypedCallback, tmpArrayCallback, container);
|
||
axisData[rowCol] = container[rowCol];
|
||
}
|
||
return axisData[rowCol][elementType];
|
||
};
|
||
|
||
/**
|
||
* @param {Worksheet} ws
|
||
* @param {boolean} bHor
|
||
* @param {number} rowCol
|
||
* @param {(value: LookUpElement, i: number) => any} savingValueCallback
|
||
* @param {(value: any) => number} tmpToTypedCallback
|
||
* @param {(value: {cElementType: any[]}) => void} [tmpArrayCallback]
|
||
* @param {Object} [opt_container] - Optional container object to use instead of the default axis data structure.
|
||
* @return {Uint32Array}
|
||
*/
|
||
TypedCache.prototype.generateCache = function(ws, bHor, rowCol, savingValueCallback, tmpToTypedCallback, tmpArrayCallback, opt_container) {
|
||
const wsId = ws.Get_Id();
|
||
const axisData = opt_container ? opt_container : (bHor ? this.data[wsId].horizontal : this.data[wsId].vertical);
|
||
const tmpArrays = {};
|
||
const c1 = bHor ? 0 : rowCol;
|
||
const r1 = bHor ? rowCol : 0;
|
||
const c2 = bHor ? AscCommon.gc_nMaxCol : rowCol;
|
||
const r2 = bHor ? rowCol : AscCommon.gc_nMaxRow;
|
||
const fullRange = ws.getRange3(r1, c1, r2, c2);
|
||
fullRange._foreachNoEmpty(function (cell, r, c) {
|
||
const value = checkTypeCell(cell, true);
|
||
if (!tmpArrays[value.type]) {
|
||
tmpArrays[value.type] = [];
|
||
}
|
||
const valueToSave = savingValueCallback(value, bHor ? c : r)
|
||
tmpArrays[value.type].push(valueToSave);
|
||
});
|
||
if (tmpArrayCallback) {
|
||
tmpArrayCallback(tmpArrays);
|
||
}
|
||
return this.saveRange(axisData, rowCol, tmpArrays, tmpToTypedCallback);
|
||
};
|
||
/**
|
||
* @param {TypedCacheAxis} axisData
|
||
* @param {number} rowColIndex
|
||
* @param {{number: any[]}} tmpArrays
|
||
* @param {(value: any) => number} tmpToTypedCallback
|
||
* @return {{cElementType: Uint32Array}}
|
||
*/
|
||
TypedCache.prototype.saveRange = function(axisData, rowColIndex, tmpArrays, tmpToTypedCallback) {
|
||
const res = axisData[rowColIndex];
|
||
for (let elementType in tmpArrays) {
|
||
const elements = tmpArrays[elementType];
|
||
const typed = new Uint32Array(elements.length);
|
||
for (let i = 0; i < elements.length; i += 1) {
|
||
typed[i] = tmpToTypedCallback(elements[i]);
|
||
}
|
||
res[elementType] = typed;
|
||
}
|
||
return axisData[rowColIndex];
|
||
};
|
||
|
||
TypedCache.prototype.clean = function() {
|
||
this.data = {};
|
||
}
|
||
|
||
VHLOOKUPCache.prototype._get = function (range, valueForSearching, arg3Value, opt_arg4, opt_arg5) {
|
||
var res, _this = this, wsId = range.getWorksheet().getId();
|
||
var opt_xlookup = opt_arg4 !== undefined;
|
||
|
||
var sRangeName;
|
||
AscCommonExcel.executeInR1C1Mode(false, function () {
|
||
sRangeName = wsId + g_cCharDelimiter + range.getName();
|
||
});
|
||
const ws = range.getWorksheet();
|
||
var cacheElem = this.cacheId[sRangeName];
|
||
if (!cacheElem) {
|
||
cacheElem = {results: {}};
|
||
}
|
||
var sInputKey;
|
||
if (!opt_xlookup) {
|
||
sInputKey =
|
||
valueForSearching.getValue() + g_cCharDelimiter + arg3Value + g_cCharDelimiter + valueForSearching.type;
|
||
} else {
|
||
sInputKey = valueForSearching.getValue() + g_cCharDelimiter + opt_arg4 + g_cCharDelimiter + opt_arg5 +
|
||
g_cCharDelimiter + valueForSearching.type;
|
||
}
|
||
res = cacheElem.results[sInputKey];
|
||
const rowCol = this.bHor ? range.bbox.r1 : range.bbox.c1
|
||
if (!res) {
|
||
const startIndex = this.bHor ? range.bbox.c1 : range.bbox.r1;
|
||
const endIndex = this.bHor ? range.bbox.c2 : range.bbox.r2;
|
||
cacheElem.results[sInputKey] =
|
||
res = this._calculate(valueForSearching,
|
||
arg3Value,
|
||
opt_arg4,
|
||
opt_arg5,
|
||
null,
|
||
ws,
|
||
rowCol,
|
||
startIndex,
|
||
endIndex);
|
||
}
|
||
return res;
|
||
};
|
||
VHLOOKUPCache.prototype._compareValues = function (val1, val2, op, opt_arg4) {
|
||
if (opt_arg4 === 2 && val2.type === cElementType.string) {
|
||
let matchingInfo = AscCommonExcel.matchingValue(val1);
|
||
return AscCommonExcel.matching(val2, matchingInfo)
|
||
} else {
|
||
let res = _func[val1.type][val2.type](val1, val2, op, null, null, true);
|
||
return res;
|
||
}
|
||
};
|
||
/**
|
||
* Compare data types (numbers < strings < booleans < errors)
|
||
*
|
||
* @private
|
||
* @param {LookUpElement} val1 - First value to compare
|
||
* @param {LookUpElement} val2 - Second value to compare
|
||
* @return {number} Negative if val1.type < val2.type, positive if val1.type > val2.type, zero if equal
|
||
*/
|
||
VHLOOKUPCache.prototype._compareTypes = function (val1, val2) {
|
||
return val1.type - val2.type;
|
||
};
|
||
/**
|
||
* A simple linear traversal of a column or row.
|
||
* When the specified XLOOKUP parameter (opt_arg4) is set,
|
||
* it also retains the nearest larger or smaller element
|
||
* than the specified value.
|
||
*
|
||
* @private
|
||
* @param {LookUpElement} valueForSearching
|
||
* @param {boolean} revert
|
||
* @param {Worksheet} ws
|
||
* @param {number} startIndex
|
||
* @param {number} endIndex
|
||
* @param {number} rowCol
|
||
* @param {LookUpElement[]} [opt_array]
|
||
* @param {number} [opt_arg4]
|
||
* @return {number}
|
||
*/
|
||
VHLOOKUPCache.prototype._simpleSearch = function (valueForSearching, revert, ws, startIndex, endIndex, rowCol, opt_arg4, opt_array) {
|
||
const t = this;
|
||
let resultIndex = -1;
|
||
let nearestIndex = -1;
|
||
let nearestValue = null;
|
||
const getValue = function (index) {
|
||
if (opt_array) {
|
||
return opt_array[index].v;
|
||
}
|
||
const cell = ws.getCell3(t.bHor ? rowCol : index, t.bHor ? index : rowCol);
|
||
return checkTypeCell(cell, true);
|
||
}
|
||
const updateNearest = function(value, valueIndex) {
|
||
if (opt_arg4 === 1 && t._compareTypes(value, valueForSearching) >= 0 && t._compareValues(value, valueForSearching, ">")) {
|
||
if (nearestValue === null) {
|
||
nearestValue = value;
|
||
nearestIndex = valueIndex;
|
||
}
|
||
if (t._compareTypes(value, nearestValue) <= 0 && t._compareValues(value, nearestValue, "<")) {
|
||
nearestIndex = valueIndex;
|
||
nearestValue = value;
|
||
}
|
||
}
|
||
if (opt_arg4 === -1 && t._compareTypes(value, valueForSearching) <= 0 && t._compareValues(value, valueForSearching,"<")) {
|
||
if (nearestValue === null) {
|
||
nearestValue = value;
|
||
nearestIndex = valueIndex;
|
||
}
|
||
if (t._compareTypes(value, nearestValue) >= 0 && t._compareValues(value, nearestValue, ">")) {
|
||
nearestIndex = valueIndex;
|
||
nearestValue = value;
|
||
}
|
||
}
|
||
}
|
||
if (revert) {
|
||
for (let i = endIndex; i >= startIndex; i -= 1) {
|
||
const val = getValue(i);
|
||
if (val.type === valueForSearching.type && this._compareValues(valueForSearching, val, "=", opt_arg4)) {
|
||
resultIndex = opt_array ? opt_array[i].i : i;
|
||
break;
|
||
}
|
||
if (opt_arg4 !== undefined) {
|
||
updateNearest(val, i);
|
||
}
|
||
}
|
||
} else {
|
||
for (let i = startIndex; i <= endIndex; i += 1) {
|
||
const val = getValue(i);
|
||
if (val.type === valueForSearching.type && this._compareValues(valueForSearching, val, "=", opt_arg4)) {
|
||
resultIndex = opt_array ? opt_array[i].i : i;
|
||
break;
|
||
}
|
||
if (opt_arg4 !== undefined) {
|
||
updateNearest(val, i);
|
||
}
|
||
}
|
||
}
|
||
if (resultIndex === -1) {
|
||
resultIndex = nearestIndex;
|
||
}
|
||
return resultIndex;
|
||
};
|
||
/**
|
||
* Traversal of a pre-saved and sorted typed array using binary search.
|
||
* Used only for exact match lookups in VLOOKUP, HLOOKUP, or XLOOKUP when opt_arg4 is set to 0.
|
||
* @private
|
||
* @param {Map} map
|
||
* @param {LookUpElement} valueForSearching
|
||
* @param {boolean} revert
|
||
* @param {Worksheet} ws
|
||
* @param {number} rowCol
|
||
* @param {number} startIndex
|
||
* @param {number} endIndex
|
||
* @return {number}
|
||
*/
|
||
VHLOOKUPCache.prototype._indexedBinarySearch = function (map, valueForSearching, revert, ws, rowCol, startIndex, endIndex) {
|
||
const searchValue = valueForSearching.value;
|
||
if (!map.has(searchValue)) {
|
||
return -1;
|
||
}
|
||
const arr = map.get(searchValue);
|
||
let left = 0;
|
||
let right = arr.length - 1;
|
||
let resultIndex = -1;
|
||
if (revert) {
|
||
while (left <= right) {
|
||
const mid = Math.floor((left + right) / 2);
|
||
if (arr[mid] < startIndex) {
|
||
left = mid + 1;
|
||
} else if (arr[mid] > endIndex) {
|
||
right = mid - 1;
|
||
} else {
|
||
resultIndex = arr[mid];
|
||
left = mid + 1;
|
||
}
|
||
}
|
||
} else {
|
||
while (left <= right) {
|
||
const mid = Math.floor((left + right) / 2);
|
||
if (arr[mid] < startIndex) {
|
||
left = mid + 1;
|
||
} else if (arr[mid] > endIndex) {
|
||
right = mid - 1;
|
||
} else {
|
||
resultIndex = arr[mid];
|
||
right = mid - 1;
|
||
}
|
||
}
|
||
}
|
||
return resultIndex;
|
||
};
|
||
/**
|
||
* Binary search down a typed array that returns the position of the nearest element with the same type as the target element.
|
||
* @private
|
||
* @param {number} currentIndex
|
||
* @param {Uint32Array} typed
|
||
* @param {number} currentEnd
|
||
* @return {number}
|
||
*/
|
||
VHLOOKUPCache.prototype._findNextCorrectType = function(currentIndex, typed, currentEnd) {
|
||
let i = 0;
|
||
let j = typed.length - 1;
|
||
let result = currentIndex;
|
||
// Binary search for first element >= currentIndex
|
||
while (i <= j) {
|
||
let k = Math.floor((i + j) / 2);
|
||
if (typed[k] < currentIndex) {
|
||
i = k + 1;
|
||
} else {
|
||
j = k - 1;
|
||
}
|
||
}
|
||
// i > j and points to first element >= currentIndex
|
||
if (i < typed.length && typed[i] <= currentEnd) {
|
||
result = typed[i];
|
||
}
|
||
return result;
|
||
};
|
||
/**
|
||
* A method that retrieves an element in a typed array by row or column index.
|
||
* @private
|
||
* @param {number} currentIndex
|
||
* @param {Uint32Array} typed
|
||
* @return {number}
|
||
*/
|
||
VHLOOKUPCache.prototype._findIndexInTyped = function(currentIndex, typed) {
|
||
let i = 0;
|
||
let j = typed.length - 1
|
||
let foundCurrent = -1;
|
||
while (i <= j) {
|
||
let k = Math.floor((i + j) / 2);
|
||
if (typed[k] === currentIndex) {
|
||
foundCurrent = k;
|
||
break;
|
||
} else if (typed[k] < currentIndex) {
|
||
i = k + 1;
|
||
} else {
|
||
j = k - 1;
|
||
}
|
||
}
|
||
return foundCurrent;
|
||
};
|
||
/**
|
||
* Finds the last occurrence of the same value in a sorted typed array.
|
||
* This function is crucial for Excel's VLOOKUP/HLOOKUP approximate match behavior,
|
||
* which requires returning the position of the LAST duplicate value when exact matches exist.
|
||
*
|
||
* Uses binary search to efficiently locate the rightmost occurrence of a value
|
||
* within the specified range boundaries.
|
||
*
|
||
* @private
|
||
* @param {number} currentIndexInTyped - The index in the typed array where the first match was found
|
||
* @param {Uint32Array} typed - Sorted array containing worksheet row/column indices of cells with matching data types
|
||
* @param {Uint32Array} typedMap - Mapping array where each element represents a unique value group identifier, used for efficient duplicate detection
|
||
* @param {number} endIndex - The maximum allowed index boundary for the search range
|
||
* @return {number} The worksheet row/column index of the last occurrence of the same value within the specified range
|
||
*/
|
||
VHLOOKUPCache.prototype._findLastSame = function(currentIndexInTyped, typed, typedMap, endIndex) {
|
||
const currentValue = typedMap[currentIndexInTyped];
|
||
let i = currentIndexInTyped + 1;
|
||
let j = typedMap.length - 1
|
||
let res = currentIndexInTyped;
|
||
let resultIndex = typed[currentIndexInTyped];
|
||
while (i <= j) {
|
||
let k = Math.floor((i + j) / 2);
|
||
if (typedMap[k] > currentValue || typed[k] > endIndex) {
|
||
j = k - 1;
|
||
} else {
|
||
i = k + 1;
|
||
res = k;
|
||
}
|
||
}
|
||
if (typedMap[res] === currentValue) {
|
||
resultIndex = typed[res];
|
||
}
|
||
return resultIndex;
|
||
};
|
||
/**
|
||
* Performs a binary search to find the position of a value in a sorted range or array.
|
||
* This is the default binary search implementation used for approximate match lookups
|
||
* in VLOOKUP and HLOOKUP functions when the range_lookup parameter is TRUE.
|
||
*
|
||
* The function searches for the largest value that is less than or equal to the lookup value.
|
||
* If an exact match is found, it returns the position of the last occurrence of that value.
|
||
* The search is optimized using typed arrays for better performance with large datasets.
|
||
*
|
||
* @private
|
||
* @param {LookUpElement} valueForSearching - The value to search for in the range/array
|
||
* @param {number} startIndex - The starting index of the search range (inclusive)
|
||
* @param {number} endIndex - The ending index of the search range (inclusive)
|
||
* @param {Worksheet} ws - The worksheet containing the data to search
|
||
* @param {number} rowCol - The row (for VLOOKUP) or column (for HLOOKUP) index to search in
|
||
* @param {Uint32Array} typed - Pre-sorted typed array containing indices of cells with matching data types
|
||
* @param {Uint32Array} typedMap - Mapping array for efficient value comparison during binary search
|
||
* @param {{i: number, v: LookUpElement}[]} [opt_array] - Optional array of objects with index and value properties, used when searching in arrays instead of worksheet ranges
|
||
* @return {number} The index of the found element (0-based), or -1 if no suitable match is found. For approximate matches, returns the index of the largest value that is less than or equal to the search value.
|
||
*/
|
||
VHLOOKUPCache.prototype._defaultBinarySearch = function (valueForSearching, startIndex, endIndex, ws, rowCol, typed, typedMap, opt_array) {
|
||
let i = startIndex;
|
||
let j = endIndex;
|
||
const t = this;
|
||
const getValue = function (index) {
|
||
if (opt_array) {
|
||
return opt_array[index].v;
|
||
}
|
||
const cell = ws.getCell3(t.bHor ? rowCol : index, t.bHor ? index : rowCol);
|
||
return checkTypeCell(cell, true);
|
||
}
|
||
let resultIndex = -1;
|
||
while (i <= j) {
|
||
let k = Math.floor((i + j) / 2);
|
||
// Check if we've gone beyond the bounds of the typed array
|
||
if (k > typed[typed.length - 1]) {
|
||
j = k - 1;
|
||
continue;
|
||
}
|
||
|
||
let val = getValue(k);
|
||
// IMPORTANT FEATURE: Check data type matching
|
||
// In Excel, only values of the same type are compared (number with number, string with string)
|
||
if (val.type !== valueForSearching.type) {
|
||
// If types don't match, find nearest element with correct type
|
||
k = this._findNextCorrectType(k, typed, j);
|
||
val = getValue(k);
|
||
}
|
||
|
||
// Main comparison logic:
|
||
// If current value is greater than searched value OR types don't match,
|
||
// narrow search to left half
|
||
if (val.type !== valueForSearching.type || this._compareValues(val, valueForSearching, ">")) {
|
||
j = k - 1;
|
||
} else {
|
||
// If current value is less than or equal to searched value,
|
||
// remember it as potential result
|
||
resultIndex = k;
|
||
|
||
// KEY FEATURE: if exact match is found,
|
||
// search for the LAST occurrence of this value in the range
|
||
if (this._compareValues(val, valueForSearching, "=")) {
|
||
// Find position in typed array
|
||
let currentIndexInTyped = this._findIndexInTyped(k, typed);
|
||
if (currentIndexInTyped !== -1) {
|
||
// Search for last occurrence of the same value
|
||
resultIndex = this._findLastSame(currentIndexInTyped, typed, typedMap, endIndex);
|
||
}
|
||
break;
|
||
}
|
||
|
||
// If no exact match, continue searching in right half
|
||
// to find largest value that is <= searched value
|
||
i = k + 1;
|
||
}
|
||
}
|
||
|
||
// Final result processing:
|
||
// If working with array, return original element index
|
||
if (opt_array && resultIndex >= 0 && resultIndex < opt_array.length) {
|
||
resultIndex = opt_array[resultIndex].i;
|
||
}
|
||
|
||
return resultIndex;
|
||
};
|
||
/**
|
||
* Performs a specialized binary search for XLOOKUP function with support for different match modes.
|
||
* This function implements Excel's XLOOKUP binary search behavior, which differs from standard
|
||
* VLOOKUP/HLOOKUP by supporting exact matches, next larger/smaller value searches, and reverse search direction.
|
||
* @private
|
||
* @param {LookUpElement} valueForSearching - The value to search for in the range/array
|
||
* @param {boolean} revert - Whether to search in reverse direction (from end to start)
|
||
* @param {number} opt_arg4 - Match mode: 0 = exact, 1 = exact or next larger, -1 = exact or next smaller
|
||
* @param {number} startIndex - The starting index of the search range (inclusive)
|
||
* @param {number} endIndex - The ending index of the search range (inclusive)
|
||
* @param {Worksheet} ws - The worksheet containing the data to search (null if using opt_array)
|
||
* @param {number} rowCol - The row (for VLOOKUP) or column (for HLOOKUP) index to search in
|
||
* @param {LookUpElement[]} opt_array - Optional array of objects with index and value properties for array-based search
|
||
* @return {number} The index of the found element, or -1 if no suitable match is found according to the specified match mode
|
||
*/
|
||
VHLOOKUPCache.prototype._xlookupBinarySearch = function (valueForSearching, revert, opt_arg4, startIndex, endIndex, ws, rowCol, opt_array) {
|
||
let i = startIndex;
|
||
let j = endIndex;
|
||
const t = this;
|
||
const getValue = function (index) {
|
||
if (opt_array) {
|
||
return opt_array[index].v;
|
||
}
|
||
const cell = ws.getCell3(t.bHor ? rowCol : index, t.bHor ? index : rowCol);
|
||
return checkTypeCell(cell, true);
|
||
}
|
||
|
||
let resultNearest = -1; // Stores index of nearest value (for approximate matches)
|
||
let resultIndex = -1; // Stores index of exact match
|
||
|
||
while (i <= j) {
|
||
const k = Math.floor((i + j) / 2);
|
||
const val = getValue(k);
|
||
|
||
// Compare data types first (numbers < strings < booleans < errors in Excel)
|
||
const typeComparison = this._compareTypes(val, valueForSearching);
|
||
if (typeComparison < 0 || (typeComparison === 0 && this._compareValues(val, valueForSearching, "<", opt_arg4))) {
|
||
revert ? j = k - 1: i = k + 1;
|
||
if (opt_arg4 === -1) {
|
||
resultNearest = k;
|
||
}
|
||
} else {
|
||
if (typeComparison === 0 && this._compareValues(valueForSearching, val, "=", opt_arg4)) {
|
||
resultIndex = k; // Found exact match
|
||
}
|
||
if (opt_arg4 === 1) {
|
||
resultNearest = k;
|
||
}
|
||
revert ? i = k + 1 : j = k - 1;
|
||
}
|
||
}
|
||
if (opt_arg4 && resultIndex === -1) {
|
||
resultIndex = resultNearest;
|
||
}
|
||
|
||
return resultIndex;
|
||
};
|
||
/**
|
||
* Retrieves or generates a sorted cache for exact match lookups in XLOOKUP.
|
||
* Creates a typed array containing worksheet indices sorted by cell values,
|
||
* used for efficient binary search operations when opt_arg4 is 0.
|
||
*
|
||
* @private
|
||
* @param {Worksheet} ws - The worksheet containing the data
|
||
* @param {number} rowCol - Row (VLOOKUP) or column (HLOOKUP) index
|
||
* @param {cElementType} type - Data type to filter and cache
|
||
* @return {Uint32Array} Sorted array of worksheet indices for the specified data type
|
||
*/
|
||
VHLOOKUPCache.prototype._getSortedCache = function(ws, rowCol, type, startIndex, endIndex, bHor) {
|
||
return this.sortedCache.getData(ws, rowCol, type, startIndex, endIndex, bHor);
|
||
};
|
||
/**
|
||
* Retrieves or generates a typed cache containing worksheet indices for approximate match lookups.
|
||
* Creates a typed array of row/column indices for cells of the specified data type,
|
||
* used in VLOOKUP/HLOOKUP binary search operations.
|
||
*
|
||
* @private
|
||
* @param {Worksheet} ws - The worksheet containing the data
|
||
* @param {number} rowCol - Row (VLOOKUP) or column (HLOOKUP) index
|
||
* @param {cElementType} type - Data type to filter and cache
|
||
* @return {Uint32Array} Array of worksheet indices for cells of the specified type
|
||
*/
|
||
VHLOOKUPCache.prototype._getTypedCache = function(ws, rowCol, type) {
|
||
return this.typedCache.getCache(ws, this.bHor, rowCol, type, function(value, index) {
|
||
return index;
|
||
}, function (value) {
|
||
return value;
|
||
});
|
||
};
|
||
/**
|
||
* Retrieves or generates a values mapping cache for efficient duplicate detection in binary search.
|
||
* Creates a typed array where each element represents a unique value group identifier,
|
||
* used to quickly find the last occurrence of duplicate values in VLOOKUP/HLOOKUP operations.
|
||
*
|
||
* @private
|
||
* @param {Worksheet} ws - The worksheet containing the data
|
||
* @param {number} rowCol - Row (VLOOKUP) or column (HLOOKUP) index
|
||
* @param {cElementType} type - Data type to filter and cache
|
||
* @return {Uint32Array} Array mapping each cell position to its value group identifier
|
||
*/
|
||
VHLOOKUPCache.prototype._getTypedCacheValuesMap = function(ws, rowCol, type) {
|
||
const t = this;
|
||
let idx = 0;
|
||
let lastCellValue = null;
|
||
return this.typedCacheValuesMap.getCache(ws, this.bHor, rowCol, type, function(value) {
|
||
if (lastCellValue !== null && t._compareValues(lastCellValue, value, '<>')) {
|
||
idx += 1;
|
||
}
|
||
lastCellValue = value;
|
||
return idx;
|
||
}, function (value) {
|
||
return value;
|
||
});
|
||
};
|
||
/**
|
||
* Main calculation method that routes to appropriate search algorithm based on function type and parameters.
|
||
* Handles LOOKUP, VLOOKUP, HLOOKUP, and XLOOKUP search operations by selecting the optimal search strategy
|
||
* (simple linear search, binary search, or specialized V/HLOOKUP binary search) based on the input parameters.
|
||
*
|
||
* @private
|
||
* @param {LookUpElement} valueForSearching - The value to search for
|
||
* @param {boolean} lookup - True for VLOOKUP/HLOOKUP approximate match, false for exact match
|
||
* @param {number} [opt_arg4] - XLOOKUP match mode (0=exact, 1=exact or larger, -1=exact or smaller)
|
||
* @param {number} [opt_arg5] - XLOOKUP search mode (1=first to last, -1=last to first, 2=binary ascending, -2=binary descending)
|
||
* @param {LookUpElement[]} [opt_array] - Optional array for array-based search instead of worksheet
|
||
* @param {Worksheet} ws - Worksheet containing the data (null if using opt_array)
|
||
* @param {number} rowCol - Row (VLOOKUP) or column (HLOOKUP) index to search in
|
||
* @param {number} startIndex - Starting index of search range
|
||
* @param {number} endIndex - Ending index of search range
|
||
* @return {number} Index of found element or -1 if not found
|
||
*/
|
||
VHLOOKUPCache.prototype._calculate = function (valueForSearching, lookup, opt_arg4, opt_arg5, opt_array, ws, rowCol, startIndex, endIndex) {
|
||
const t = this;
|
||
let res = -1;
|
||
let xlookup = opt_arg4 !== undefined && opt_arg5 !== undefined;
|
||
const revert = opt_arg5 < 0;
|
||
|
||
if (valueForSearching.type === cElementType.string) {
|
||
valueForSearching = new cString(valueForSearching.getValue().toLowerCase());
|
||
}
|
||
|
||
if (xlookup) {
|
||
if (Math.abs(opt_arg5) === 1) {
|
||
if (opt_array) {
|
||
res = this._simpleSearch(valueForSearching, revert, ws, startIndex, endIndex, rowCol,opt_arg4, opt_array);
|
||
} else if (opt_arg4 === 0) {
|
||
const sorted = this._getSortedCache(ws, rowCol, valueForSearching.type, startIndex, endIndex, this.bHor);
|
||
if (sorted) {
|
||
res = this._indexedBinarySearch(sorted, valueForSearching, revert, ws, rowCol, startIndex, endIndex);
|
||
}
|
||
} else {
|
||
res = this._simpleSearch(valueForSearching, revert, ws,startIndex, endIndex, rowCol, opt_arg4);
|
||
}
|
||
} else if (Math.abs(opt_arg5) === 2) {
|
||
res = this._xlookupBinarySearch(valueForSearching, revert, opt_arg4, startIndex, endIndex, ws, rowCol, opt_array);
|
||
}
|
||
} else if (lookup) {
|
||
const typedCache = this._getTypedCache(ws, rowCol, valueForSearching.type);
|
||
const typedCacheValuesMap = this._getTypedCacheValuesMap(ws, rowCol, valueForSearching.type);
|
||
if (typedCache) {
|
||
res = this._defaultBinarySearch(valueForSearching, startIndex, endIndex, ws, rowCol, typedCache, typedCacheValuesMap);
|
||
}
|
||
} else {
|
||
if (opt_array) {
|
||
res = this._simpleSearch(valueForSearching, false, ws, startIndex, endIndex, rowCol,opt_arg4, opt_array);
|
||
} else {
|
||
const sorted = this._getSortedCache(ws, rowCol, valueForSearching.type, startIndex, endIndex, this.bHor);
|
||
if (sorted) {
|
||
res = this._indexedBinarySearch(sorted, valueForSearching, false, ws, rowCol, startIndex, endIndex);
|
||
}
|
||
}
|
||
}
|
||
return res;
|
||
};
|
||
VHLOOKUPCache.prototype.remove = function (cell, dataOld, dataNew) {
|
||
var wsId = cell.ws.getId();
|
||
var cacheRange = this.cacheRanges[wsId];
|
||
if (cacheRange) {
|
||
var oGetRes = cacheRange.get(new Asc.Range(cell.nCol, cell.nRow, cell.nCol, cell.nRow));
|
||
for (var i = 0, length = oGetRes.all.length; i < length; ++i) {
|
||
var elem = oGetRes.all[i];
|
||
elem.data.results = {};
|
||
}
|
||
}
|
||
this.sortedCache.changeData(cell, dataOld, dataNew);
|
||
};
|
||
VHLOOKUPCache.prototype.clean = function () {
|
||
this.cacheId = {};
|
||
this.cacheRanges = {};
|
||
this.sortedCache = new TypedMapCache();
|
||
this.typedCache.clean();
|
||
this.typedCacheValuesMap.clean();
|
||
};
|
||
function MatchCache() {
|
||
VHLOOKUPCache.call(this);
|
||
}
|
||
|
||
MatchCache.prototype = Object.create(VHLOOKUPCache.prototype);
|
||
MatchCache.prototype.constructor = MatchCache;
|
||
MatchCache.prototype.calculate = function (arg, _arg1) {
|
||
let arg0 = arg[0], arg1 = arg[1], arg2, arg3;
|
||
let isXMatch = arg[4];
|
||
|
||
let argError = cBaseFunction.prototype._checkErrorArg.call(this, arg);
|
||
if (argError) {
|
||
return argError;
|
||
}
|
||
|
||
if(isXMatch) {
|
||
if (cElementType.empty === arg1.type) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
// default values for XMatch
|
||
arg2 = arg[2] ? arg[2] : new cNumber(0);
|
||
arg3 = arg[3] ? arg[3] : new cNumber(1);
|
||
} else {
|
||
if (cElementType.array !== arg1.type && cElementType.cellsRange !== arg1.type && cElementType.cellsRange3D !== arg1.type) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
// default values for Match
|
||
arg2 = arg[2] ? arg[2] : new cNumber(1);
|
||
arg3 = new cNumber(1);
|
||
}
|
||
|
||
if (cElementType.cellsRange3D === arg0.type || cElementType.cellsRange === arg0.type) {
|
||
// TODO пересмотреть поведение функции при получении массива первым аргументом
|
||
arg0 = isXMatch ? arg0.getFullArray().getElementRowCol(0,0) : arg0.cross(_arg1);
|
||
|
||
if (cElementType.empty === arg0.type) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
} else if (cElementType.array === arg0.type) {
|
||
arg0 = arg0.getElementRowCol(0,0);
|
||
} else if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
let _valueArg0 = arg0.getValue();
|
||
if (cElementType.error === _valueArg0.type) {
|
||
return _valueArg0;
|
||
}
|
||
} else if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
|
||
let a2Value;
|
||
if (cElementType.array === arg2.type || cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type) {
|
||
a2Value = arg2.getFirstElement();
|
||
if (!a2Value) {
|
||
a2Value = new cEmpty();
|
||
}
|
||
a2Value = a2Value.tocNumber();
|
||
} else if (cElementType.error === arg2.type) {
|
||
return arg2;
|
||
} else {
|
||
if (cElementType.cell === arg2.type || cElementType.cell3D === arg2.type) {
|
||
a2Value = arg2.getValue().tocNumber();
|
||
}
|
||
a2Value = arg2.tocNumber();
|
||
}
|
||
|
||
if (cElementType.error === a2Value.type) {
|
||
return a2Value;
|
||
}
|
||
a2Value = Math.floor(a2Value.toNumber());
|
||
|
||
if (!(-1 === a2Value || 0 === a2Value || 1 === a2Value || 2 === a2Value)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
let a3Value;
|
||
if (cElementType.array === arg3.type || cElementType.cellsRange === arg3.type || cElementType.cellsRange3D === arg3.type) {
|
||
a3Value = arg3.getFirstElement();
|
||
if (!a3Value) {
|
||
a3Value = new cEmpty();
|
||
}
|
||
a3Value = a3Value.tocNumber();
|
||
} else if (cElementType.error === arg3.type) {
|
||
return arg3;
|
||
} else {
|
||
if (cElementType.cell === arg3.type || cElementType.cell3D === arg3.type) {
|
||
a3Value = arg3.getValue().tocNumber();
|
||
}
|
||
a3Value = arg3.tocNumber();
|
||
}
|
||
|
||
if (cElementType.error === a3Value.type) {
|
||
return a3Value;
|
||
}
|
||
a3Value = Math.floor(a3Value.toNumber());
|
||
|
||
if(!(-2 === a3Value || -1 === a3Value || 1 === a3Value || 2 === a3Value)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if(cElementType.error === arg1.type) {
|
||
return arg1;
|
||
} else if (cElementType.cellsRange !== arg1.type && cElementType.cellsRange3D !== arg1.type && cElementType.array !== arg1.type) {
|
||
// if value is not array/range, make it array
|
||
let arg1Array = new cArray();
|
||
if (cElementType.cell === arg1.type || cElementType.cell3D === arg1.type) {
|
||
arg1 = arg1.getValue();
|
||
}
|
||
arg1Array.addElement(arg1);
|
||
arg1 = arg1Array;
|
||
}
|
||
|
||
if (cElementType.array === arg1.type) {
|
||
arg1 = arg1.getMatrix();
|
||
|
||
let i, a1RowCount = arg1.length, a1ColumnCount = arg1[0].length, arr;
|
||
|
||
if (a1RowCount > 1 && a1ColumnCount > 1) {
|
||
return new cError(cErrorType.not_available);
|
||
} else if (a1RowCount === 1 && a1ColumnCount >= 1) {
|
||
arr = arg1[0];
|
||
} else {
|
||
arr = [];
|
||
for (i = 0; i < a1RowCount; i++) {
|
||
arr[i] = arg1[i][0];
|
||
}
|
||
}
|
||
return isXMatch ? this._xMatchCalculate(arr, arg0, a2Value, a3Value, true) : this._calculate(arr, arg0, a2Value);
|
||
} else if (cElementType.cell === arg1.type || cElementType.cell3D === arg1.type ||
|
||
cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type) {
|
||
// add range.isonecell
|
||
let oSearchRange = arg1.getRange();
|
||
if (!oSearchRange) {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
let a1RowCount = oSearchRange.bbox.r2 - oSearchRange.bbox.r1 + 1, a1ColumnCount = oSearchRange.bbox.c2 - oSearchRange.bbox.c1 + 1;
|
||
let bHor = false;
|
||
if (a1RowCount > 1 && a1ColumnCount > 1) {
|
||
return new cError(cErrorType.not_available);
|
||
} else if (a1RowCount === 1 && a1ColumnCount >= 1) {
|
||
bHor = true;
|
||
}
|
||
|
||
return this._get(oSearchRange, arg0, a2Value, a3Value, bHor, isXMatch);
|
||
} else {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
};
|
||
MatchCache.prototype._get = function (range, arg0, arg2, arg3, bHor, isXMatch) {
|
||
let res, _this = this, wsId = range.getWorksheet().getId(),
|
||
sRangeName = wsId + g_cCharDelimiter + range.getName(), cacheElem = this.cacheId[sRangeName],
|
||
valueForSearching = arg0.getValue(),
|
||
arg2Value = arg2,
|
||
arg3Value = arg3;
|
||
|
||
if (!cacheElem) {
|
||
cacheElem = {elements: [], results: {}};
|
||
|
||
range._foreachNoEmpty(function (cell, r, c) {
|
||
cacheElem.elements.push({v: checkTypeCell(cell), i: (bHor ? c - range.bbox.c1 : r - range.bbox.r1)});
|
||
});
|
||
this.cacheId[sRangeName] = cacheElem;
|
||
let cacheRange = this.cacheRanges[wsId];
|
||
if (!cacheRange) {
|
||
cacheRange = new AscCommonExcel.RangeDataManager(null);
|
||
this.cacheRanges[wsId] = cacheRange;
|
||
}
|
||
cacheRange.add(range.getBBox0(), cacheElem);
|
||
}
|
||
let sInputKey = arg3Value ? (valueForSearching + g_cCharDelimiter + arg2Value + g_cCharDelimiter + arg3Value) : (valueForSearching + g_cCharDelimiter + arg2Value);
|
||
res = cacheElem.results[sInputKey];
|
||
if(!res && isXMatch) {
|
||
cacheElem.results[sInputKey] = res = this._xMatchCalculate(cacheElem.elements, arg0, arg2, arg3, false);
|
||
} else if (!res) {
|
||
cacheElem.results[sInputKey] = res = this._calculate(cacheElem.elements, arg0, arg2);
|
||
}
|
||
return res;
|
||
};
|
||
MatchCache.prototype._calculate = function (arr, a0, a2) {
|
||
let a2Value = a2,
|
||
a0Type = a0.type,
|
||
a0Value = a0.getValue();
|
||
|
||
if (!(cElementType.number === a0Type || cElementType.string === a0Type || cElementType.bool === a0Type ||
|
||
cElementType.error === a0Type || cElementType.empty === a0Type)) {
|
||
if(cElementType.empty === a0Value.type) {
|
||
a0Value = a0Value.tocNumber();
|
||
}
|
||
a0Type = a0Value.type;
|
||
a0Value = a0Value.getValue();
|
||
}
|
||
|
||
let item, index = -1, curIndex;
|
||
for (let i = 0; i < arr.length; ++i) {
|
||
item = undefined !== arr[i].v ? arr[i].v : arr[i];
|
||
curIndex = undefined !== arr[i].i ? arr[i].i : i;
|
||
if (item.type === a0Type) {
|
||
if (0 === a2Value) {
|
||
if (cElementType.string === a0Type) {
|
||
if (AscCommonExcel.searchRegExp2(item.toString(), a0Value)) {
|
||
index = curIndex;
|
||
break;
|
||
}
|
||
} else {
|
||
if (item == a0Value) {
|
||
index = curIndex;
|
||
break;
|
||
}
|
||
}
|
||
} else if (1 === a2Value) {
|
||
if (item <= a0Value) {
|
||
index = curIndex;
|
||
} else {
|
||
break;
|
||
}
|
||
} else if (-1 === a2Value) {
|
||
if (item >= a0Value) {
|
||
index = curIndex;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return (-1 < index) ? new cNumber(index + 1) : new cError(cErrorType.not_available);
|
||
};
|
||
MatchCache.prototype._xMatchCalculate = function (arr, a0, a2, a3, isArray) {
|
||
let a0Type,
|
||
a0Value,
|
||
a2Value = a2,
|
||
a3Value = a3,
|
||
tempArr = arr.slice();
|
||
|
||
if (a0.type === cElementType.cell || a0.type === cElementType.cell3D) {
|
||
a0Type = a0.getValue().type;
|
||
a0Value = a0.getValue().getValue();
|
||
} else {
|
||
a0Type = a0.type;
|
||
a0Value = a0.getValue();
|
||
}
|
||
|
||
if (!(cElementType.number === a0Type || cElementType.string === a0Type || cElementType.bool === a0Type ||
|
||
cElementType.error === a0Type || cElementType.empty === a0Type)) {
|
||
if(cElementType.empty === a0Value.type) {
|
||
a0Value = a0Value.tocNumber();
|
||
}
|
||
a0Type = a0Value.type;
|
||
a0Value = a0Value.getValue();
|
||
}
|
||
|
||
let item, index = -1, curIndex, moreEqualArr, lessEqualArr;
|
||
|
||
if (a3Value === 1 || a3Value === -1) {
|
||
if (isArray) {
|
||
// make array universal
|
||
tempArr = tempArr.map(function (item, index) {
|
||
return {
|
||
v: item,
|
||
i: index,
|
||
}
|
||
});
|
||
}
|
||
|
||
if (a3Value === -1) {
|
||
tempArr.reverse();
|
||
}
|
||
|
||
// exact search
|
||
for (let i = 0; i < tempArr.length; ++i) {
|
||
item = tempArr[i].v ? tempArr[i].v : tempArr[i];
|
||
curIndex = tempArr[i].i ? tempArr[i].i : i;
|
||
if (item.type === a0Type) {
|
||
if (cElementType.string === a0Type) {
|
||
if (AscCommonExcel.searchRegExp2(item.toString(), a0Value)) {
|
||
index = curIndex;
|
||
break;
|
||
}
|
||
} else {
|
||
if (item.getValue() === a0Value) {
|
||
index = curIndex;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// approximate search
|
||
if (a2Value === 1 && index === -1) {
|
||
// looking for the smallest value of those that are greater than the looking
|
||
moreEqualArr = tempArr.filter(function(item) {
|
||
if (a0Type === cElementType.number) {
|
||
if (item.v.type === a0Type) {
|
||
return item.v.getValue() >= a0Value;
|
||
} else if (item.v.type === cElementType.string) {
|
||
return item.v;
|
||
}
|
||
} else if (a0Type === cElementType.string) {
|
||
if (item.v.type === a0Type) {
|
||
return item.v.getValue() >= a0Value;
|
||
} else if (item.v.type === cElementType.bool) {
|
||
return item.v;
|
||
}
|
||
} else if (a0Type === cElementType.bool) {
|
||
if (item.v.type === a0Type) {
|
||
return item.v.getValue() >= a0Value;
|
||
} else if (item.v.type === cElementType.error || item.v.type === cElementType.empty) {
|
||
return item.v;
|
||
}
|
||
}
|
||
});
|
||
|
||
moreEqualArr.sort(function(a, b) {
|
||
if (cElementType.number === a0Type) {
|
||
if (cElementType.string === a.v.type && cElementType.string === b.v.type) {
|
||
return AscCommonExcel.stringCompare(a.v.getValue(), b.v.getValue());
|
||
} else if (cElementType.number === a.v.type && cElementType.number === b.v.type) {
|
||
return a.v.getValue() - b.v.getValue();
|
||
} else if (cElementType.error === a.v.type || cElementType.error === b.v.type) {
|
||
return 0;
|
||
} else {
|
||
return cElementType.string === b.v.type ? -1 : 1;
|
||
}
|
||
} else if (cElementType.string === a0Type) {
|
||
if (cElementType.string === a.v.type && cElementType.string === b.v.type) {
|
||
return AscCommonExcel.stringCompare(a.v.getValue(), b.v.getValue());
|
||
} else if (cElementType.string === a.v.type || cElementType.string === b.v.type) {
|
||
return 1;
|
||
} else if (cElementType.bool === a.v.type && cElementType.bool === b.v.type) {
|
||
if (a.v.getValue() !== b.v.getValue()) {
|
||
return a.v.getValue() > b.v.getValue() ? 1 : -1;
|
||
}
|
||
return 0;
|
||
}
|
||
} else if (cElementType.bool === a0Type) {
|
||
// cElementType.bool == 2, cElementType.empty == 4, cElementType.error == 3
|
||
if (a.v.type > b.v.type) {
|
||
return 1;
|
||
}
|
||
if (a.v.type < b.v.type) {
|
||
return -1;
|
||
}
|
||
if (a.v.type === cElementType.bool) {
|
||
return 0;
|
||
} else if (a.v.type === cElementType.error) {
|
||
return 0;
|
||
} else if (a.v.type === cElementType.empty) {
|
||
return a.i - b.i;
|
||
}
|
||
}
|
||
});
|
||
index = moreEqualArr.length > 0 ? moreEqualArr[0].i : index;
|
||
} else if (a2Value === -1 && index === -1) {
|
||
// looking for the largest value of those that are smaller than the looking
|
||
if (cElementType.empty === a0Type) {
|
||
// special search mode for empty cell where error > bool > string > number
|
||
const priorityArr = tempArr.map(function(item) {
|
||
const getTypePriority = function(element) {
|
||
switch (element.v.type) {
|
||
case cElementType.error:
|
||
return 4;
|
||
case cElementType.bool:
|
||
return 3;
|
||
case cElementType.string:
|
||
return 2;
|
||
case cElementType.number:
|
||
return 1;
|
||
default:
|
||
return Infinity;
|
||
}
|
||
};
|
||
|
||
const typePriority = getTypePriority(item);
|
||
|
||
return {
|
||
v: item.v,
|
||
i: item.i,
|
||
priority: typePriority,
|
||
};
|
||
}).sort(function(a,b) {
|
||
if (a.priority === b.priority) {
|
||
if (a.v.getValue() === b.v.getValue()) {
|
||
if (a3Value === -1) {
|
||
return a.i > b.i ? -1 : 1;
|
||
} else if (a3Value === 1) {
|
||
return 0;
|
||
}
|
||
}
|
||
return a.v.getValue() > b.v.getValue() ? -1 : 1;
|
||
}
|
||
return a.priority > b.priority ? -1 : 1;
|
||
});
|
||
|
||
index = priorityArr.length > 0 ? priorityArr[0].i : index;
|
||
} else {
|
||
lessEqualArr = tempArr.filter(function(item) {
|
||
if (a0Type === cElementType.number) {
|
||
if (item.v.type === a0Type) {
|
||
return item.v.getValue() <= a0Value;
|
||
}
|
||
} else if (a0Type === cElementType.string) {
|
||
if (item.v.type === a0Type) {
|
||
return item.v.getValue() <= a0Value;
|
||
} else if (item.v.type === cElementType.number) {
|
||
return item.v;
|
||
}
|
||
} else if (a0Type === cElementType.bool) {
|
||
if (item.v.type === a0Type) {
|
||
return item.v <= a0Value;
|
||
} else if (item.v.type === cElementType.string) {
|
||
return item.v;
|
||
}
|
||
}
|
||
});
|
||
|
||
lessEqualArr.sort(function(a, b) {
|
||
if (cElementType.number === a0Type) {
|
||
return b.v.getValue() - a.v.getValue();
|
||
} else if (cElementType.string === a0Type) {
|
||
if (cElementType.string === a.v.type && cElementType.string === b.v.type) {
|
||
return AscCommonExcel.stringCompare(a.v.getValue(), b.v.getValue());
|
||
} else if (cElementType.string === a.v.type || cElementType.string === b.v.type) {
|
||
return 1;
|
||
} else if (cElementType.number === a.v.type && cElementType.number === b.v.type) {
|
||
return a.v.getValue() > b.v.getValue() ? -1 : 1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
} else if (cElementType.bool === a0Type) {
|
||
if (cElementType.bool === a.v.type && cElementType.bool === b.v.type) {
|
||
return -1;
|
||
} else if (cElementType.string === a.v.type && cElementType.string === b.v.type) {
|
||
if (a.v.getValue() === b.v.getValue()) {
|
||
if (a3Value === 1) {
|
||
return a.i - b.i;
|
||
} else if(a3Value === -1) {
|
||
return b.i - a.i;
|
||
}
|
||
}
|
||
return a.v.getValue() > b.v.getValue() ? -1 : 1;
|
||
} else if (cElementType.bool === a.v.type) {
|
||
return -1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
});
|
||
|
||
index = lessEqualArr.length > 0 ? lessEqualArr[0].i : index;
|
||
}
|
||
}
|
||
|
||
} else if (a3Value === 2) {
|
||
if (2 === a2Value) {
|
||
// wildcard match(err)
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
index = XBinarySearch(a0Value, tempArr, a2Value, false);
|
||
} else if (a3Value === -2) {
|
||
if (2 === a2Value) {
|
||
// wildcard match(err)
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
index = XBinarySearch(a0Value, tempArr, a2Value, true);
|
||
}
|
||
|
||
return (-1 < index) ? new cNumber(index + 1) : new cError(cErrorType.not_available);
|
||
};
|
||
|
||
function LOOKUPCache() {
|
||
VHLOOKUPCache.call(this);
|
||
}
|
||
|
||
LOOKUPCache.prototype = Object.create(VHLOOKUPCache.prototype);
|
||
LOOKUPCache.prototype.constructor = LOOKUPCache;
|
||
|
||
LOOKUPCache.prototype.calculate = function (arg) {
|
||
function compareValues (val1, val2) {
|
||
if (val1.type !== val2.type) {
|
||
return false;
|
||
} else if (val1.type === cElementType.string) {
|
||
let str1 = val1.toString().toLowerCase();
|
||
let str2 = val2.toString().toLowerCase();
|
||
|
||
if (str1 >= str2) {
|
||
return true
|
||
}
|
||
|
||
return false;
|
||
} else if (val1.value >= val2.value) {
|
||
return true
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function arrFinder(arr) {
|
||
if (arr.getRowCount() > arr.getCountElementInRow()) {
|
||
// searching in the first column
|
||
resC = arr.getCountElementInRow() > 1 ? 1 : 0;
|
||
let arrCol = arr.getCol(0);
|
||
resR = _func.lookupBinarySearch(arg0, arrCol, false);
|
||
} else {
|
||
// searching in the first row
|
||
resR = arr.getRowCount() > 1 ? 1 : 0;
|
||
let arrRow = arr.getRow(0);
|
||
resC = _func.lookupBinarySearch(arg0, arrRow, false);
|
||
}
|
||
}
|
||
|
||
function findIndexInArray(lookingValue, arr) {
|
||
let resIndex = -1;
|
||
|
||
if (arr.getRowCount() >= arr.getCountElementInRow()) {
|
||
// searching in the first column and return elem with same position (index) from last column
|
||
let arrCol = arr.getCol(0);
|
||
resIndex = _func.lookupBinarySearch(lookingValue, arrCol, false);
|
||
} else {
|
||
// searching in the first row and return elem with same position (index) from last row
|
||
let arrRow = arr.getRow(0);
|
||
resIndex = _func.lookupBinarySearch(lookingValue, arrRow, false);
|
||
}
|
||
|
||
return resIndex;
|
||
}
|
||
|
||
// .calculate - base args checks, dimensions checks and got to ._get func
|
||
// ._get - get noEmpty elements from range and add to cache, then get typed array and add calculation result to the cache
|
||
// ._calculate - calculate result(binary search)
|
||
let arrayMode = arg.length === 2 ? true : false;
|
||
let arg0 = arg[0], arg1 = arg[1], arg2 = 2 === arg.length ? arg1 : arg[2], resC = -1, resR = -1,
|
||
t = this, res, arg2SingleElem;
|
||
|
||
/* arg0 (looking value) check */
|
||
if (cElementType.cellsRange === arg0.type || cElementType.cellsRange3D === arg0.type) {
|
||
if (arg0.isOneElement()) {
|
||
arg0 = arg0.getFirstElement();
|
||
} else {
|
||
arg0 = new cError(cErrorType.wrong_value_type);
|
||
}
|
||
} else if (cElementType.array === arg0.type) {
|
||
arg0 = arg0.getElementRowCol(0, 0);
|
||
}
|
||
|
||
if (cElementType.cell === arg0.type || cElementType.cell3D === arg0.type) {
|
||
arg0 = arg0.getValue();
|
||
}
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
if (cElementType.empty === arg0.type) {
|
||
arg0 = arg0.tocNumber();
|
||
}
|
||
|
||
/* arg1 (looking array) check */
|
||
if (arg1.type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
if ((arg1.type === cElementType.cellsRange || arg1.type === cElementType.cellsRange3D || arg1.type === cElementType.array) && arg1.isOneElement()) {
|
||
arg1 = arg1.getFirstElement();
|
||
}
|
||
if (arg1.type === cElementType.error) {
|
||
return arg1;
|
||
}
|
||
|
||
/* arg2 (return array) check */
|
||
if (arg2.type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
if ((arg2.type === cElementType.cellsRange || arg2.type === cElementType.cellsRange3D || arg2.type === cElementType.array) && arg2.isOneElement()) {
|
||
arg2 = arg2.getFirstElement();
|
||
}
|
||
if (arg2.type === cElementType.error) {
|
||
return arg2;
|
||
}
|
||
|
||
if (!(cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type || cElementType.array === arg2.type)) {
|
||
arg2SingleElem = arg2;
|
||
}
|
||
|
||
// variants check:
|
||
/* arg1 is not array/area */
|
||
if ( !(cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type || cElementType.array === arg1.type) ) {
|
||
if (arg1.type === cElementType.cell || arg1.type === cElementType.cell3D) {
|
||
arg1 = arg1.getValue();
|
||
}
|
||
if (arg1.type === cElementType.error) {
|
||
return arg1;
|
||
}
|
||
|
||
if (cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type || cElementType.array === arg2.type) {
|
||
if (cElementType.array === arg2.type && (arg2.getRowCount() > 1 && arg2.getCountElementInRow() > 1)) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
arg2 = arg2.getFirstElement();
|
||
}
|
||
if (arg2.type === cElementType.cell || arg2.type === cElementType.cell3D) {
|
||
arg2 = arg2.getValue();
|
||
}
|
||
if (arg2.type === cElementType.error) {
|
||
return arg2;
|
||
}
|
||
|
||
// compare values
|
||
let res = compareValues(arg0, arg1);
|
||
if (res) {
|
||
return arg2;
|
||
}
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
|
||
if (cElementType.array === arg1.type && cElementType.array === arg2.type) { /* arg1 & arg2 is arrays */
|
||
let lookingIndex = -1;
|
||
let arg1Rows = arg1.getRowCount(),
|
||
arg2Rows = arg2.getRowCount(),
|
||
arg1Cols = arg1.getCountElementInRow(),
|
||
arg2Cols = arg2.getCountElementInRow();
|
||
|
||
/* check two dimensional array but only in vector form */
|
||
if (!arrayMode && (((arg1Rows < arg2Rows) && arg2Cols > 1) || (arg1Rows > 1 && arg1Cols > 1 && arg2Rows > 1 && arg2Cols > 1))) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
// arrFinder(arg1);
|
||
lookingIndex = findIndexInArray(arg0, arg1);
|
||
|
||
let byRow, byCol;
|
||
if (arg1Rows >= arg1Cols) {
|
||
// search by col
|
||
byCol = true
|
||
} else {
|
||
byRow = true
|
||
}
|
||
|
||
if (lookingIndex === -1) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
if (arg2Rows === 1) {
|
||
if (lookingIndex >= arg2.getCountElementInRow()) {
|
||
return new cError(cErrorType.not_available)
|
||
}
|
||
return arg2.getElementRowCol(0, lookingIndex);
|
||
} else if (arg2Cols === 1) {
|
||
if (lookingIndex >= arg2.rowCount) {
|
||
return new cError(cErrorType.not_available)
|
||
}
|
||
return arg2.getElementRowCol(lookingIndex, 0);
|
||
} else {
|
||
// return from last row/col
|
||
return arg2.getElementRowCol(byCol ? lookingIndex : arg2Rows - 1, byCol ? arg2Cols - 1 : lookingIndex);
|
||
}
|
||
|
||
} else if (cElementType.array === arg1.type || cElementType.array === arg2.type) { /* arg1 || arg2 is array */
|
||
let lookingIndex = -1;
|
||
let _arg1 = cElementType.array === arg1.type ? arg1 : arg1.getFullArray(); // !!! slow
|
||
|
||
// find resR & resC position in array/area
|
||
// arrFinder(_arg1); // old solution with finding resR and resC
|
||
lookingIndex = findIndexInArray(arg0, _arg1);
|
||
|
||
if (lookingIndex < 0) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
if (arg2SingleElem) {
|
||
return lookingIndex === 0 ? arg2SingleElem : new cError(cErrorType.not_available);
|
||
}
|
||
|
||
let byRow, byCol;
|
||
let dimension = arg2.getDimensions ? arg2.getDimensions() : null;
|
||
if (dimension) {
|
||
if (dimension.row >= dimension.col) {
|
||
byCol = true
|
||
} else {
|
||
byRow = true
|
||
}
|
||
|
||
if (dimension.row === 1) {
|
||
return arg2.getValueByRowCol ? arg2.getValueByRowCol(0, lookingIndex, true) : arg2.getElementRowCol(0, lookingIndex);
|
||
} else if (dimension.col === 1) {
|
||
return arg2.getValueByRowCol ? arg2.getValueByRowCol(lookingIndex, 0, true) : arg2.getElementRowCol(lookingIndex, 0);
|
||
} else {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
} else {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
} else { /* arg1 & arg2 is area */
|
||
if (cElementType.cellsRange3D === arg1.type && !arg1.isSingleSheet() ||
|
||
cElementType.cellsRange3D === arg2.type && !arg2.isSingleSheet()) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
let arg2RowsLength;
|
||
let bbox;
|
||
if (cElementType.cellsRange3D === arg1.type) {
|
||
bbox = arg1.bbox;
|
||
} else if (cElementType.cellsRange === arg1.type) {
|
||
bbox = arg1.range.bbox;
|
||
}
|
||
|
||
if (cElementType.cellsRange3D === arg2.type) {
|
||
arg2RowsLength = arg2.bbox.r2 - arg2.bbox.r1 + 1;
|
||
} else if (cElementType.cellsRange === arg2.type) {
|
||
arg2RowsLength = arg2.range.bbox.r2 - arg2.range.bbox.r1 + 1;
|
||
}
|
||
|
||
|
||
let bVertical = bbox.r2 - bbox.r1 >= bbox.c2 - bbox.c1;
|
||
let index;
|
||
if(index === undefined) {
|
||
let ws = arg1.getWS(),
|
||
r = bVertical ? bbox.r2 : bbox.r1,
|
||
c = bVertical ? bbox.c1 : bbox.c2;
|
||
let oSearchRange = ws.getRange3(bbox.r1, bbox.c1, r, c);
|
||
t.bHor = bVertical ? false : true;
|
||
|
||
index = this._get(/* searchRange */oSearchRange, arg0, true);
|
||
|
||
if (index === undefined || index < 0) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
}
|
||
|
||
|
||
let ws = cElementType.cellsRange3D === arg1.type && arg1.isSingleSheet() ? arg1.getWS() : arg1.ws;
|
||
if (cElementType.cellsRange3D === arg1.type) {
|
||
if (arg1.isSingleSheet()) {
|
||
ws = arg1.getWS();
|
||
} else {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
} else if (cElementType.cellsRange === arg1.type) {
|
||
ws = arg1.getWS();
|
||
} else {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
|
||
if (arg2SingleElem) {
|
||
return arg2SingleElem;
|
||
}
|
||
|
||
AscCommonExcel.executeInR1C1Mode(false, function () {
|
||
let b = arg2.getBBox0();
|
||
|
||
if (2 === arg.length) {
|
||
if (!bVertical) {
|
||
// res = new cRef(ws.getCell3(b.r2, b.c1 + index).getName(), ws);
|
||
res = new cRef(ws.getCell3(b.r2, Math.abs(bbox.c1 - b.c1) + index).getName(), ws);
|
||
} else {
|
||
// res = new cRef(ws.getCell3(b.r1 + index, b.c2).getName(), ws);
|
||
res = new cRef(ws.getCell3(Math.abs(bbox.r1 - b.r1) + index, b.c2).getName(), ws);
|
||
}
|
||
} else {
|
||
if (1 === arg2RowsLength) {
|
||
// res = new cRef(ws.getCell3(b.r2, b.c1 + index).getName(), ws);
|
||
res = new cRef(ws.getCell3(b.r2, Math.abs(bbox.c1 - b.c1) + index).getName(), ws);
|
||
} else {
|
||
// res = new cRef(ws.getCell3(b.r1 + index, b.c2).getName(), ws);
|
||
res = new cRef(ws.getCell3(Math.abs(bbox.r1 - b.r1) + index, b.c2).getName(), ws);
|
||
}
|
||
}
|
||
});
|
||
return res;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cVLOOKUP() {
|
||
}
|
||
|
||
cVLOOKUP.prototype = Object.create(cBaseFunction.prototype);
|
||
cVLOOKUP.prototype.constructor = cVLOOKUP;
|
||
cVLOOKUP.prototype.name = 'VLOOKUP';
|
||
cVLOOKUP.prototype.argumentsMin = 3;
|
||
cVLOOKUP.prototype.argumentsMax = 4;
|
||
cVLOOKUP.prototype.arrayIndexes = {/*0: 1,*/ 1: 1, /*2: 1*/};
|
||
cVLOOKUP.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
// cVLOOKUP.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.value_replace_area;
|
||
cVLOOKUP.prototype.argumentsType = [argType.any, argType.number, argType.number, argType.logical];
|
||
cVLOOKUP.prototype.Calculate = function (arg) {
|
||
let retArr = new cArray();
|
||
let error = false;
|
||
|
||
if (arg[0].type === cElementType.array) {
|
||
if (arg[2] && arg[2].type === cElementType.cellsRange || arg[2].type === cElementType.cellsRange3D || arg[2].type === cElementType.array) {
|
||
arg[2] = arg[2].getValue2(0,0);
|
||
}
|
||
let dimension = arg[0].getDimensions();
|
||
for (let r = 0; r < dimension.row; r++) {
|
||
retArr.addRow();
|
||
for (let c = 0; c < dimension.col; c++) {
|
||
retArr.addElement(g_oVLOOKUPCache.calculate([arg[0].getValue2(r, c), arg[1], arg[2], arg[3]], arguments[1]));
|
||
}
|
||
}
|
||
|
||
return retArr;
|
||
}
|
||
|
||
if (arg[2] && (arg[2].type === cElementType.array)) {
|
||
let dimension = arg[2].getDimensions();
|
||
for (let r = 0; r < dimension.row; r++) {
|
||
retArr.addRow();
|
||
for (let c = 0; c < dimension.col; c++) {
|
||
if (!error) {
|
||
let res = g_oVLOOKUPCache.calculate([arg[0], arg[1], arg[2].getValue2(r, c), arg[3]], arguments[1]);
|
||
if (res.type === cElementType.error) {
|
||
error = true;
|
||
}
|
||
retArr.addElement(res);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return error ? new cError(cErrorType.bad_reference) : retArr
|
||
}
|
||
|
||
return g_oVLOOKUPCache.calculate(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cXLOOKUP() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cXLOOKUP.prototype = Object.create(cBaseFunction.prototype);
|
||
cXLOOKUP.prototype.constructor = cXLOOKUP;
|
||
cXLOOKUP.prototype.name = 'XLOOKUP';
|
||
cXLOOKUP.prototype.argumentsMin = 3;
|
||
cXLOOKUP.prototype.argumentsMax = 6;
|
||
cXLOOKUP.prototype.arrayIndexes = {1: 1, 2: 1};
|
||
cXLOOKUP.prototype.argumentsType = [argType.any, argType.reference, argType.reference, argType.any, argType.number, argType.number];
|
||
cXLOOKUP.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.value_replace_area;
|
||
cXLOOKUP.prototype.isXLFN = true;
|
||
cXLOOKUP.prototype.Calculate = function (arg) {
|
||
|
||
let arg0 = arg[0], arg1 = arg[1], arg2 = arg[2];
|
||
let arg3 = arg[3], arg4 = arg[4], arg5 = arg[5];
|
||
|
||
if (cElementType.error === arg0.type) {
|
||
return arg0;
|
||
}
|
||
if (cElementType.cell === arg1.type) {
|
||
arg1 = arg1.getValue();
|
||
}
|
||
if (cElementType.number === arg1.type || cElementType.string === arg1.type || cElementType.error === arg1.type || cElementType.cell === arg1.type) {
|
||
const arr1 = new cArray();
|
||
arr1.addElement(arg1);
|
||
arg1 = arr1;
|
||
const arr2 = new cArray();
|
||
arr2.addElement(arg2);
|
||
arg2 = arr2;
|
||
}
|
||
|
||
if (!((cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type ||
|
||
cElementType.array === arg1.type) &&
|
||
(cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type ||
|
||
cElementType.array === arg2.type))) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
//[if_not_found]
|
||
if (!arg3 || arg3.type === cElementType.empty) {
|
||
arg3 = new cError(cErrorType.not_available);
|
||
}
|
||
|
||
//arg4/arg5 - только число
|
||
//[match_mode]
|
||
//0 - If none found, return #N/A. This is the default.
|
||
//-1 - If none found, return the next smaller item.
|
||
//1 - If none found, return the next larger item.
|
||
//2 - A wildcard match where *, ?, and ~
|
||
//TODO если аргумент массив/area - результат становится размером с этот массив
|
||
//TODO либо обрабатывать выше и вызывать эту функцию для каждого элемента массива, либо здесь вычислять этот массив
|
||
if (!arg4) {
|
||
arg4 = new cNumber(0);
|
||
}
|
||
arg4 = arg4.tocNumber();
|
||
if (cElementType.error === arg4.type) {
|
||
return arg4;
|
||
} else {
|
||
arg4 = parseInt(arg4.toNumber());
|
||
if (!(arg4 >= -1 && arg4 <= 2)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
//TODO если аргумент массив/area - результат становится размером с этот массив
|
||
//TODO либо обрабатывать выше и вызывать эту функцию для каждого элемента массива, либо здесь вычислять этот массив
|
||
//[search_mode]
|
||
//1 - Perform a search starting at the first item. This is the default.
|
||
//-1 - Perform a reverse search starting at the last item.
|
||
//2 - Perform a binary search that relies on lookup_array being sorted in ascending order. If not sorted, invalid results will be returned.
|
||
//-2 - Perform a binary search that relies on lookup_array being sorted in descending order. If not sorted, invalid results will be returned.
|
||
if (!arg5) {
|
||
arg5 = new cNumber(1);
|
||
}
|
||
arg5 = arg5.tocNumber();
|
||
if (cElementType.error === arg5.type) {
|
||
return arg5;
|
||
} else {
|
||
arg5 = parseInt(arg5.toNumber());
|
||
if (!(arg5 >= -2 && arg5 <= 2)) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
}
|
||
|
||
//массив arg1 должен содержать 1 строку или 1 столбец
|
||
let dimensions1 = arg1.getDimensions();
|
||
let dimensions2 = arg2.getDimensions();
|
||
let bVertical = null;
|
||
if (dimensions1 && dimensions2) {
|
||
if (dimensions1.col === 1 && dimensions2.row === dimensions1.row) {
|
||
bVertical = true;
|
||
} else if (dimensions1.row === 1 && dimensions2.col === dimensions1.col) {
|
||
bVertical = false;
|
||
}
|
||
}
|
||
|
||
if (bVertical === null) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
let res, arrayOffset = 0;
|
||
if (bVertical) {
|
||
res = g_oVLOOKUPCache.calculate([arg0, arg1, null, null, arg4, arg5], arguments[1]);
|
||
} else {
|
||
res = g_oHLOOKUPCache.calculate([arg0, arg1, null, null, arg4, arg5], arguments[1]);
|
||
}
|
||
|
||
if (res === -1) {
|
||
return arg3;
|
||
} else {
|
||
if (res.type && res.type === cElementType.error) {
|
||
return res;
|
||
}
|
||
|
||
if (arg1.type === cElementType.array) {
|
||
if (dimensions2.bbox) {
|
||
arrayOffset = bVertical ? dimensions2.bbox.r1 : dimensions2.bbox.c1;
|
||
res += arrayOffset;
|
||
}
|
||
}
|
||
//возвращаем из arg2 строку или столбец
|
||
let _startRange = 0;
|
||
if (dimensions2.bbox) {
|
||
_startRange = bVertical ? dimensions2.bbox.r1 : dimensions2.bbox.c1;
|
||
} else if (dimensions1.bbox) {
|
||
_startRange = bVertical ? dimensions1.bbox.r1 : dimensions1.bbox.c1;
|
||
}
|
||
|
||
if (cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type) {
|
||
let _r1 = !bVertical ? dimensions2.bbox.r1 : res - _startRange + dimensions2.bbox.r1;
|
||
let _cl = bVertical ? dimensions2.bbox.c1 : res - _startRange + dimensions2.bbox.c1;
|
||
let _r2 = !bVertical ? dimensions2.bbox.r2 : res - _startRange + dimensions2.bbox.r1;
|
||
let _c2 = bVertical ? dimensions2.bbox.c2 : res - _startRange + dimensions2.bbox.c1;
|
||
let _range = new Asc.Range(_cl, _r1, _c2, _r2);
|
||
|
||
let _res;
|
||
let rangeName;
|
||
AscCommonExcel.executeInR1C1Mode(false, function () {
|
||
rangeName = _range.getName();
|
||
});
|
||
if (cElementType.cellsRange === arg2.type) {
|
||
_res = _range.isOneCell() ? new cRef(rangeName, arg2.getWS()) : new cArea(rangeName, arg2.getWS());
|
||
} else {
|
||
_res = _range.isOneCell() ? new cRef3D(rangeName, arg2.getWS()) : new cArea3D(rangeName, arg2.getWS());
|
||
}
|
||
|
||
return _res;
|
||
} else {
|
||
let _length = !bVertical ? dimensions2.row : dimensions2.col;
|
||
let _array = new cArray();
|
||
for (let i = 0; i < _length; i++) {
|
||
let _row = !bVertical ? i : res - _startRange;
|
||
let _col = bVertical ? i : res - _startRange;
|
||
let _elem = arg2.getElementRowCol ? arg2.getElementRowCol(_row, _col) : arg2.getValueByRowCol(_row, _col, true);
|
||
if (!bVertical) {
|
||
_array.addRow();
|
||
_array.addElement(_elem);
|
||
} else {
|
||
_array.addElement(_elem);
|
||
}
|
||
}
|
||
|
||
return _array;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cVSTACK() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cVSTACK.prototype = Object.create(cBaseFunction.prototype);
|
||
cVSTACK.prototype.constructor = cVSTACK;
|
||
cVSTACK.prototype.name = 'VSTACK';
|
||
cVSTACK.prototype.argumentsMin = 1;
|
||
cVSTACK.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cVSTACK.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cVSTACK.prototype.argumentsType = [[argType.reference]];
|
||
cVSTACK.prototype.isXLFN = true;
|
||
cVSTACK.prototype.Calculate = function (arg) {
|
||
let unionArray;
|
||
for (let i = 0; i < arg.length; i++) {
|
||
let matrix;
|
||
if (arg[i].type === cElementType.cellsRange || arg[i].type === cElementType.array || arg[i].type === cElementType.cell || arg[i].type === cElementType.cell3D) {
|
||
matrix = arg[i].getMatrix();
|
||
} else if (arg[i].type === cElementType.cellsRange3D) {
|
||
if (arg[i].isSingleSheet()) {
|
||
matrix = arg[i].getMatrix()[0];
|
||
} else {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
} else if (arg[i].type === cElementType.error) {
|
||
return arg[i];
|
||
} else if (arg[i].type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
matrix = [[arg[i]]];
|
||
}
|
||
|
||
//добавляем по строкам
|
||
for (let j = 0; j < matrix.length; j++) {
|
||
if (matrix[j]) {
|
||
if (!unionArray) {
|
||
unionArray = [];
|
||
}
|
||
unionArray.push(matrix[j]);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (unionArray) {
|
||
let res = new cArray();
|
||
res.fillFromArray(unionArray);
|
||
res.fillMatrix(new cError(cErrorType.not_available));
|
||
return res;
|
||
} else {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cHSTACK() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cHSTACK.prototype = Object.create(cBaseFunction.prototype);
|
||
cHSTACK.prototype.constructor = cHSTACK;
|
||
cHSTACK.prototype.name = 'HSTACK';
|
||
cHSTACK.prototype.argumentsMin = 1;
|
||
cHSTACK.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cHSTACK.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cHSTACK.prototype.argumentsType = [[argType.reference]];
|
||
cHSTACK.prototype.isXLFN = true;
|
||
cHSTACK.prototype.Calculate = function (arg) {
|
||
let unionArray;
|
||
let startCol = 0;
|
||
for (let i = 0; i < arg.length; i++) {
|
||
let matrix;
|
||
if (arg[i].type === cElementType.cellsRange || arg[i].type === cElementType.array || arg[i].type === cElementType.cell || arg[i].type === cElementType.cell3D) {
|
||
matrix = arg[i].getMatrix();
|
||
} else if (arg[i].type === cElementType.cellsRange3D) {
|
||
if (arg[i].isSingleSheet()) {
|
||
matrix = arg[i].getMatrix()[0];
|
||
} else {
|
||
return new cError(cErrorType.bad_reference);
|
||
}
|
||
} else if (arg[i].type === cElementType.error) {
|
||
return arg[i];
|
||
} else if (arg[i].type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
} else {
|
||
matrix = [[arg[i]]];
|
||
}
|
||
|
||
let maxColCount = 0;
|
||
for (let j = 0; j < matrix.length; j++) {
|
||
if (matrix[j]) {
|
||
maxColCount = Math.max(maxColCount, matrix[j].length);
|
||
for (let k = 0; k < matrix[j].length; k++) {
|
||
if (matrix[j][k]) {
|
||
if (!unionArray) {
|
||
unionArray = [];
|
||
}
|
||
if (!unionArray[j]) {
|
||
unionArray[j] = [];
|
||
}
|
||
unionArray[j][k + startCol] = matrix[j][k];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
startCol += maxColCount;
|
||
}
|
||
|
||
if (unionArray) {
|
||
let res = new cArray();
|
||
res.fillFromArray(unionArray);
|
||
res.fillMatrix(new cError(cErrorType.not_available));
|
||
return res;
|
||
} else {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
};
|
||
|
||
function toRowCol(arg, argument1, toCol) {
|
||
var argError = cBaseFunction.prototype._checkErrorArg.call(this, arg);
|
||
if (argError) {
|
||
return argError;
|
||
}
|
||
|
||
//из документации:
|
||
//Excel returns a #VALUE! when an array constant contains one or more numbers that are not a whole number.
|
||
//не повторил в мс
|
||
|
||
let arg1 = arg[0];
|
||
if (arg1.type === arg1.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
if (arg1.type === cElementType.cellsRange3D) {
|
||
arg1 = arg1.toArray()[0];
|
||
} else {
|
||
arg1 = arg1.toArray();
|
||
}
|
||
|
||
//Excel returns a #NUM when array is too large.
|
||
let elemCount = arg1.length * arg1[0].length;
|
||
if (elemCount > 1048578) {
|
||
return new cError(cErrorType.not_available);
|
||
}
|
||
|
||
//0 Keep all values (default)
|
||
//1 Ignore blanks
|
||
//2 Ignore errors
|
||
//3 Ignore blanks and errors
|
||
let arg2 = arg[1] ? arg[1] : new cNumber(0);
|
||
if (cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type) {
|
||
arg2 = arg2.cross(argument1);
|
||
} else if (cElementType.array === arg2.type) {
|
||
arg2 = arg2.getElementRowCol(0, 0);
|
||
}
|
||
arg2 = arg2.tocNumber();
|
||
if (arg2.type === cElementType.error) {
|
||
return arg2;
|
||
}
|
||
arg2 = arg2.toNumber();
|
||
|
||
//scan_by_column
|
||
let arg3 = arg[2] ? arg[2] : new cBool(false);
|
||
if (cElementType.cellsRange === arg3.type || cElementType.cellsRange3D === arg3.type) {
|
||
arg3 = arg3.cross(argument1);
|
||
} else if (cElementType.array === arg3.type) {
|
||
arg3 = arg3.getElementRowCol(0, 0);
|
||
}
|
||
arg3 = arg3.tocBool();
|
||
if (arg3.type === cElementType.error) {
|
||
return arg3;
|
||
}
|
||
arg3 = arg3.toBool();
|
||
|
||
let arg1_array = new cArray();
|
||
arg1_array.fillFromArray(arg1);
|
||
|
||
|
||
var res = new cArray();
|
||
arg1_array.foreach2(function (elem, r, c) {
|
||
if (elem) {
|
||
let needAdd = true;
|
||
switch (arg2) {
|
||
case 0:
|
||
break;
|
||
case 1:
|
||
if (elem.type === cElementType.empty) {
|
||
needAdd = false;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (elem.type === cElementType.error) {
|
||
needAdd = false;
|
||
}
|
||
break;
|
||
case 3:
|
||
if (elem.type === cElementType.error || elem.type === cElementType.empty) {
|
||
needAdd = false;
|
||
}
|
||
break;
|
||
}
|
||
if (needAdd) {
|
||
if (toCol) {
|
||
res.addRow();
|
||
}
|
||
res.addElement(elem);
|
||
}
|
||
}
|
||
}, arg3);
|
||
|
||
return res;
|
||
}
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cTOROW() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cTOROW.prototype = Object.create(cBaseFunction.prototype);
|
||
cTOROW.prototype.constructor = cTOROW;
|
||
cTOROW.prototype.name = 'TOROW';
|
||
cTOROW.prototype.argumentsMin = 1;
|
||
cTOROW.prototype.argumentsMax = 3;
|
||
cTOROW.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cTOROW.prototype.arrayIndexes = {0: 1};
|
||
cTOROW.prototype.argumentsType = [argType.reference, argType.number, argType.bool];
|
||
cTOROW.prototype.isXLFN = true;
|
||
cTOROW.prototype.Calculate = function (arg) {
|
||
return toRowCol(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cTOCOL() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cTOCOL.prototype = Object.create(cBaseFunction.prototype);
|
||
cTOCOL.prototype.constructor = cTOCOL;
|
||
cTOCOL.prototype.name = 'TOCOL';
|
||
cTOCOL.prototype.argumentsMin = 1;
|
||
cTOCOL.prototype.argumentsMax = 3;
|
||
cTOCOL.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cTOCOL.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cTOCOL.prototype.argumentsType = [argType.reference, argType.number, argType.bool];
|
||
cTOCOL.prototype.isXLFN = true;
|
||
cTOCOL.prototype.Calculate = function (arg) {
|
||
return toRowCol(arg, arguments[1], true);
|
||
};
|
||
|
||
function wrapRowsCols(arg, argument1, toCol) {
|
||
let argError = cBaseFunction.prototype._checkErrorArg.call(this, arg);
|
||
if (argError) {
|
||
return argError;
|
||
}
|
||
|
||
let arg1 = arg[0];
|
||
if (arg1.type === cElementType.empty) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
let arg0Dimensions = arg1.getDimensions();
|
||
if (arg0Dimensions.col > 1 && arg0Dimensions.row > 1) {
|
||
return new cError(cErrorType.wrong_value_type);
|
||
}
|
||
|
||
let arg2 = arg[1];
|
||
if (cElementType.cellsRange === arg2.type || cElementType.cellsRange3D === arg2.type) {
|
||
arg2 = arg2.getValueByRowCol(0,0);
|
||
} else if (cElementType.array === arg2.type) {
|
||
arg2 = arg2.getElementRowCol(0, 0);
|
||
} else if (arg2.type === cElementType.empty) {
|
||
return new cError(cErrorType.not_numeric);
|
||
}
|
||
if (!arg2) {
|
||
arg2 = new cEmpty();
|
||
}
|
||
|
||
arg2 = arg2.tocNumber();
|
||
if (arg2.type === cElementType.error) {
|
||
return arg2;
|
||
}
|
||
arg2 = arg2.toNumber();
|
||
|
||
if (arg2 < 1) {
|
||
return new cError(cErrorType.not_numeric);
|
||
}
|
||
|
||
let arg3 = arg[2] ? arg[2] : new cError(cErrorType.not_available);
|
||
if (cElementType.cellsRange === arg3.type || cElementType.cellsRange3D === arg3.type) {
|
||
arg3 = arg3.getValueByRowCol(0,0,true);
|
||
} else if (cElementType.array === arg3.type) {
|
||
arg3 = arg3.getElementRowCol(0, 0);
|
||
}
|
||
|
||
let res = new cArray();
|
||
if (cElementType.cellsRange === arg1.type || cElementType.cellsRange3D === arg1.type || cElementType.array === arg1.type) {
|
||
let rowCounter = 0, colCounter = 0;
|
||
arg1.foreach2(function (val) {
|
||
if (toCol) {
|
||
/*if (res.array.l && res.array[res.array.length - 1].length === arg2) {
|
||
res.addRow();
|
||
}*/
|
||
if (rowCounter === arg2) {
|
||
colCounter++;
|
||
rowCounter = 0;
|
||
}
|
||
if (!res.array[rowCounter]) {
|
||
res.array[rowCounter] = [];
|
||
}
|
||
res.array[rowCounter][colCounter] = val;
|
||
rowCounter++;
|
||
} else {
|
||
if (res.array[res.array.length - 1] && res.array[res.array.length - 1].length === arg2) {
|
||
res.addRow();
|
||
}
|
||
res.addElement(val);
|
||
}
|
||
});
|
||
if (toCol) {
|
||
res.recalculate();
|
||
}
|
||
res.fillMatrix(arg3);
|
||
} else {
|
||
if (cElementType.cell === arg1.type || cElementType.cell3D === arg1.type) {
|
||
arg1 = arg1.getValue();
|
||
}
|
||
res.addElement(arg1);
|
||
}
|
||
return res;
|
||
}
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cWRAPROWS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cWRAPROWS.prototype = Object.create(cBaseFunction.prototype);
|
||
cWRAPROWS.prototype.constructor = cWRAPROWS;
|
||
cWRAPROWS.prototype.name = 'WRAPROWS';
|
||
cWRAPROWS.prototype.argumentsMin = 2;
|
||
cWRAPROWS.prototype.argumentsMax = 3;
|
||
cWRAPROWS.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cWRAPROWS.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cWRAPROWS.prototype.argumentsType = [argType.any/*vector*/, argType.number, argType.any];
|
||
cWRAPROWS.prototype.isXLFN = true;
|
||
cWRAPROWS.prototype.Calculate = function (arg) {
|
||
return wrapRowsCols(arg, arguments[1]);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cWRAPCOLS() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cWRAPCOLS.prototype = Object.create(cBaseFunction.prototype);
|
||
cWRAPCOLS.prototype.constructor = cWRAPCOLS;
|
||
cWRAPCOLS.prototype.name = 'WRAPCOLS';
|
||
cWRAPCOLS.prototype.argumentsMin = 2;
|
||
cWRAPCOLS.prototype.argumentsMax = 3;
|
||
cWRAPCOLS.prototype.numFormat = AscCommonExcel.cNumFormatNone;
|
||
cWRAPCOLS.prototype.returnValueType = AscCommonExcel.cReturnFormulaType.array;
|
||
cWRAPCOLS.prototype.argumentsType = [argType.any/*vector*/, argType.number, argType.any];
|
||
cWRAPCOLS.prototype.isXLFN = true;
|
||
cWRAPCOLS.prototype.Calculate = function (arg) {
|
||
return wrapRowsCols(arg, arguments[1], true);
|
||
};
|
||
|
||
/**
|
||
* @constructor
|
||
* @extends {AscCommonExcel.cBaseFunction}
|
||
*/
|
||
function cXMATCH() {
|
||
}
|
||
|
||
//***array-formula***
|
||
cXMATCH.prototype = Object.create(cBaseFunction.prototype);
|
||
cXMATCH.prototype.constructor = cXMATCH;
|
||
cXMATCH.prototype.name = 'XMATCH';
|
||
cXMATCH.prototype.argumentsMin = 2;
|
||
cXMATCH.prototype.argumentsMax = 4;
|
||
cXMATCH.prototype.arrayIndexes = {1: 1};
|
||
cXMATCH.prototype.argumentsType = [argType.any, argType.reference, argType.number, argType.number];
|
||
cXMATCH.prototype.isXLFN = true;
|
||
cXMATCH.prototype.Calculate = function (arg) {
|
||
arg[4] = true;
|
||
return g_oMatchCache.calculate(arg, arguments[1]);
|
||
};
|
||
|
||
var g_oVLOOKUPCache = new VHLOOKUPCache(false);
|
||
var g_oHLOOKUPCache = new VHLOOKUPCache(true);
|
||
var g_oMatchCache = new MatchCache();
|
||
var g_oLOOKUPCache = new LOOKUPCache();
|
||
|
||
//----------------------------------------------------------export----------------------------------------------------
|
||
window['AscCommonExcel'] = window['AscCommonExcel'] || {};
|
||
window['AscCommonExcel'].g_oLOOKUPCache = g_oLOOKUPCache;
|
||
window['AscCommonExcel'].g_oVLOOKUPCache = g_oVLOOKUPCache;
|
||
window['AscCommonExcel'].g_oHLOOKUPCache = g_oHLOOKUPCache;
|
||
window['AscCommonExcel'].g_oMatchCache = g_oMatchCache;
|
||
})(window);
|