Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,56 +83,56 @@ public int FindIndex()
private int FindIndexInternal()
{
var direction = GetLookupDirection();
int maxItems;
int maxItems;
int startOffset;
if (direction == LookupRangeDirection.Vertical)
{
maxItems = GetMaxItemsRow(_lookupRange);
maxItems = GetMaxItemsRow(_lookupRange, out startOffset);
}
else
{
//dimensionItems = _lookupRange.Dimension.ToCol - _lookupRange.Dimension.FromCol + 1;
//maxItems = _lookupRange.Size.NumberOfCols > dimensionItems ? dimensionItems : _lookupRange.Size.NumberOfCols;
maxItems = GetMaxItemsColumns(_lookupRange);
maxItems = GetMaxItemsColumns(_lookupRange, out startOffset);
}
int closestBelowIx = -1;
int closestAboveIx = -1;
object closestBelow = null;
object closestAbove = null;
// ix is relative to the clamped start (startOffset). The value read and the
// index returned are offset by startOffset so they stay relative to the
// range's own FromRow/FromCol, while iteration is limited to the populated
// area to preserve performance on open ranges (e.g. A:A).
var ix = _searchMode == LookupSearchMode.ReverseStartingAtLast ? maxItems - 1 : 0;

while (ix >= 0)
while (ix >= 0 && ix < maxItems)
{
var actualIx = startOffset + ix;
object value = direction == LookupRangeDirection.Vertical ?
_lookupRange.GetOffset(ix, 0) :
_lookupRange.GetOffset(0, ix);
_lookupRange.GetOffset(actualIx, 0) :
_lookupRange.GetOffset(0, actualIx);
var cr = _comparer.Compare(_lookupValue, value);
if (cr == 0)
{
return ix;
return actualIx;
}
else if (cr < 0)
{
if (closestAbove == null || _comparer.Compare(closestAbove, value) > 0)
{
closestAbove = value;
closestAboveIx = ix;
closestAboveIx = actualIx;
}
}
else
{
if (closestBelow == null || _comparer.Compare(closestBelow, value) < 0)
{
closestBelow = value;
closestBelowIx = ix;
closestBelowIx = actualIx;
}
}
if (_searchMode == LookupSearchMode.StartingAtFirst)
{
ix++;
if (ix >= maxItems)
{
ix = -1;
}
}
else
{
Expand All @@ -151,11 +151,14 @@ private int FindIndexInternal()
}


private int GetMaxItemsRow(IRangeInfo lookupRange)
private int GetMaxItemsRow(IRangeInfo lookupRange, out int startOffset)
{
startOffset = 0;
var adjusted = lookupRange.GetAddressDimensionAdjusted(0);
if (adjusted != null)
{
startOffset = adjusted.FromRow - lookupRange.Address.FromRow;
if (startOffset < 0) startOffset = 0;
return adjusted.ToRow - adjusted.FromRow + 1;
}
if (lookupRange.Address.ToRow > lookupRange.Dimension.ToRow)
Expand All @@ -165,11 +168,14 @@ private int GetMaxItemsRow(IRangeInfo lookupRange)
return _lookupRange.Size.NumberOfRows;
}

private int GetMaxItemsColumns(IRangeInfo lookupRange)
private int GetMaxItemsColumns(IRangeInfo lookupRange, out int startOffset)
{
startOffset = 0;
var adjusted = lookupRange.GetAddressDimensionAdjusted(0);
if (adjusted != null)
{
startOffset = adjusted.FromCol - lookupRange.Address.FromCol;
if (startOffset < 0) startOffset = 0;
return adjusted.ToCol - adjusted.FromCol + 1;
}
if (lookupRange.Address.ToCol > lookupRange.Dimension.ToCol)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,22 @@ public void ShouldFindExactMatch_StartAtFirst_Horizontal_1()
var ix = scanner.FindIndex();
Assert.AreEqual(2, ix);
}

[TestMethod]
public void ShouldFindExactMatch_WhenRangeStartsBeforeWorksheetDimension()
{
// Arrange: Leave rows 1-4 empty. Worksheet dimension will start at Row 5.
_sheet.Cells[5, 2].Value = "Apple";
_sheet.Cells[6, 2].Value = "Pear";
// Lookup range B2:B6 starts at row 2 (before worksheet dimension starts).
var ri = new RangeInfo(_sheet, _sheet.Cells["B2:B6"]);
var scanner = new XlookupScanner("Pear", ri, LookupSearchMode.StartingAtFirst, LookupMatchMode.ExactMatch);

// Act
var ix = scanner.FindIndex();

// Assert: "Pear" is at index 4 (relative to B2). In EPPlus 8.5.0+ this returns -1 instead.
Assert.AreEqual(4, ix);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -467,5 +467,46 @@ public void XlookupReturnEmptyString()
ws.Cells["A2"].Calculate();
Assert.AreEqual("", ws.Cells["A2"].Value);
}

[TestMethod]
public void ShouldFindValue_WhenRangeStartsBeforeWorksheetDimension()
{
// Data starts at row 5, but the lookup range starts at row 2 (before the
// worksheet dimension). Regression test for lookup ranges whose FromRow
// is smaller than Worksheet.Dimension.FromRow (issue in 8.5.0-8.6.3).
_sheet.Cells[5, 1].Value = "Apple";
_sheet.Cells[5, 2].Value = "+11";
_sheet.Cells[6, 1].Value = "Pear";
_sheet.Cells[6, 2].Value = "+22";

_sheet.Cells["E2"].Value = "Pear";
_sheet.Cells["F2"].Formula = "XLOOKUP(E2,A2:A6,B2:B6,\"Not found\")";

_sheet.Calculate();

Assert.AreEqual("+22", _sheet.Cells["F2"].Value.ToString());
}

[TestMethod]
[DataRow(1, "+11")] // StartingAtFirst
[DataRow(-1, "+11")] // ReverseStartingAtLast
public void ShouldFindValue_WhenOpenRangeAndDataStartsFarDown(int searchMode, string expected)
{
// Open column ranges (A:A) where the data begins far down the sheet.
// The scanner must clamp both ends to the worksheet dimension for
// performance while still returning a range-relative index, in both
// forward and reverse search mode.
_sheet.Cells[10000, 1].Value = "Apple";
_sheet.Cells[10000, 2].Value = "+11";
_sheet.Cells[10001, 1].Value = "Pear";
_sheet.Cells[10001, 2].Value = "+22";

_sheet.Cells["E2"].Value = "Apple";
_sheet.Cells["F2"].Formula = $"XLOOKUP(E2,A:A,B:B,\"Not found\", 0, {searchMode})";

_sheet.Calculate();

Assert.AreEqual(expected, _sheet.Cells["F2"].Value.ToString());
}
}
}
Loading