Size MAP to the longest argument array, not the shortest
MAP used the smallest dimensions across its argument arrays, so when the arrays had different lengths the result was clipped to the shorter one, dropping the elements only the longer array reached. Size the result to the largest argument array instead. A position one array does not reach has no value to map, so it is left as a not-available error, the way the OOXML format treats a length mismatch. Add a test that maps an addition over a three-element and a two-element array and confirms the result has three elements, the third a not-available error. Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> Change-Id: I11e756c74598de408da01cbc6844b674dda64ceb Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5607 Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> (cherry picked from commit bc64a61d28bc9b1eeee0e5c08f67a0945a2781d1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207865 Tested-by: Xisco Fauli <xiscofauli@libreoffice.org> Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
cb9b0f4550
commit
45473674e8
@@ -326,6 +326,49 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testMapPerElementErrorIsolation)
|
||||
m_pDoc->DeleteTab(0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestFormula2, testMapMismatchedArrayLengths)
|
||||
{
|
||||
// MAP over two arrays of different lengths produces a result as long as the
|
||||
// longer one, with the position the shorter array cannot reach left not
|
||||
// available, rather than clipping the result to the shorter array.
|
||||
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true);
|
||||
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
|
||||
|
||||
ScMarkData aMark(m_pDoc->GetSheetLimits());
|
||||
aMark.SelectOneTable(0);
|
||||
|
||||
// Read MAP's result matrix directly. Entered over a single cell it is
|
||||
// neither spilled nor padded, so the matrix is exactly what MAP produced.
|
||||
// Linear element access keeps the check independent of the result shape.
|
||||
m_pDoc->InsertMatrixFormula(0, 0, 0, 0, aMark,
|
||||
u"=MAP({1;2;3}; {1;2}; LAMBDA(a; b; a + b))"_ustr);
|
||||
ScFormulaCell* pCell = m_pDoc->GetFormulaCell(ScAddress(0, 0, 0));
|
||||
CPPUNIT_ASSERT(pCell);
|
||||
const ScMatrix* pMat = pCell->GetMatrix();
|
||||
CPPUNIT_ASSERT(pMat);
|
||||
|
||||
// Three elements, the length of the longer array, not two.
|
||||
SCSIZE nCols = 0;
|
||||
SCSIZE nRows = 0;
|
||||
pMat->GetDimensions(nCols, nRows);
|
||||
CPPUNIT_ASSERT_EQUAL(SCSIZE(3), nCols * nRows);
|
||||
|
||||
// The two matched elements add. The third has no second operand.
|
||||
CPPUNIT_ASSERT_EQUAL(2.0, pMat->GetDouble(0));
|
||||
CPPUNIT_ASSERT_EQUAL(4.0, pMat->GetDouble(1));
|
||||
CPPUNIT_ASSERT_EQUAL(sal_Int32(FormulaError::NotAvailable),
|
||||
sal_Int32(GetDoubleErrorValue(pMat->GetDouble(2))));
|
||||
|
||||
// The same result reduced to a scalar, as a second check: IFERROR turns the
|
||||
// not-available element into a sentinel, so a three-element result sums
|
||||
// higher than one clipped to two elements.
|
||||
m_pDoc->InsertMatrixFormula(
|
||||
2, 0, 2, 0, aMark, u"=SUM(IFERROR(MAP({1;2;3}; {1;2}; LAMBDA(a; b; a + b)); 100))"_ustr);
|
||||
CPPUNIT_ASSERT_EQUAL(106.0, m_pDoc->GetValue(ScAddress(2, 0, 0)));
|
||||
|
||||
m_pDoc->DeleteTab(0);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(TestFormula2, testFuncCHOOSE)
|
||||
{
|
||||
sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
|
||||
|
||||
@@ -1590,9 +1590,11 @@ void ScInterpreter::ScMap()
|
||||
{
|
||||
SCSIZE nCols, nRows;
|
||||
xMat->GetDimensions(nCols, nRows);
|
||||
if (nC == 0 || nCols < nC)
|
||||
// The result spans the largest argument array. A shorter array
|
||||
// leaves its missing positions not available.
|
||||
if (nCols > nC)
|
||||
nC = nCols;
|
||||
if (nR == 0 || nRows < nR)
|
||||
if (nRows > nR)
|
||||
nR = nRows;
|
||||
aMatrices[i] = xMat;
|
||||
}
|
||||
@@ -1622,8 +1624,16 @@ void ScInterpreter::ScMap()
|
||||
{
|
||||
for (SCSIZE nRow = 0; nRow < nR; ++nRow)
|
||||
{
|
||||
bool bElementMissing = false;
|
||||
for (sal_uInt8 nMat = 0; nMat < nArgCount; ++nMat)
|
||||
{
|
||||
SCSIZE nMatCols, nMatRows;
|
||||
aMatrices[nMat]->GetDimensions(nMatCols, nMatRows);
|
||||
if (nCol >= nMatCols || nRow >= nMatRows)
|
||||
{
|
||||
bElementMissing = true;
|
||||
break;
|
||||
}
|
||||
if (aMatrices[nMat]->IsEmpty(nCol, nRow))
|
||||
aArgs[nMat] = new formula::FormulaMissingToken();
|
||||
else if (aMatrices[nMat]->IsValue(nCol, nRow))
|
||||
@@ -1639,6 +1649,13 @@ void ScInterpreter::ScMap()
|
||||
else // TODO: callable
|
||||
aArgs[nMat] = new formula::FormulaErrorToken(FormulaError::IllegalParameter);
|
||||
}
|
||||
// One of the arrays is shorter and has no element here, so there is
|
||||
// nothing to map at this position.
|
||||
if (bElementMissing)
|
||||
{
|
||||
xResMat->PutError(FormulaError::NotAvailable, nCol, nRow);
|
||||
continue;
|
||||
}
|
||||
ScCall(pCallable, aArgs);
|
||||
PutMatrixValue(PopToken(), xResMat, nCol, nRow);
|
||||
// The result of this cell is stored, so clear any error it produced
|
||||
|
||||
Reference in New Issue
Block a user