Skip to content

This closes #2191, add Rows.IterateColumns to stream cells without []string allocation#2330

Open
artur-chopikian wants to merge 1 commit into
qax-os:masterfrom
artur-chopikian:master
Open

This closes #2191, add Rows.IterateColumns to stream cells without []string allocation#2330
artur-chopikian wants to merge 1 commit into
qax-os:masterfrom
artur-chopikian:master

Conversation

@artur-chopikian

@artur-chopikian artur-chopikian commented May 16, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a new Rows.IterateColumns method that streams each non-empty cell of the current row through a user-supplied callback func(colIndex int, cellValue string), instead of
returning a []string like Rows.Columns does.

Internally, the SAX parse loop in Columns is extracted into a private iterate helper that both methods share. rowXMLIterator gains a yield field; when set, rowXMLHandler
calls it per cell and skips the append(appendSpace(...), val) accumulation. When unset, the original code path runs verbatim — Columns keeps its existing behavior and pays only
a single nil check per cell.

Related Issue

Closes: #2191.

Motivation and Context

Rows.Columns allocates and grows a []string for every row, and fills middle gaps between non-empty cells with "". On wide or sparse rows this is a significant amount of work
the caller doesn't actually need — most callers immediately iterate the slice and never retain it. IterateColumns lets callers consume cells directly, eliminating the slice
allocation, the append regrowth chain, and the gap-filling loop, while preserving the existing Columns API and performance.

Usage:

rows, _ := f.Rows("Sheet1")
for rows.Next() {
    err := rows.IterateColumns(func(colIndex int, cellValue string) {
        fmt.Printf("col %d: %s\n", colIndex, cellValue)
    })
    if err != nil { /* ... */ }
}
rows.Close()

How Has This Been Tested

Added TestIterateColumns in rows_test.go covering:

  • Parity with Columns: iterates test/Book1.xlsx Sheet2 with both APIs and compares the resulting rows after trimming trailing empties.
  • Sparse data: builds a sheet with values at C1, E1, A3, B3, E3 and reconstructs the cell map from (rowIdx, colIndex) pairs — verifies column indices reflect actual
    positions, not emission order.
  • Nil callback: confirms passing nil is a safe no-op and returns no error.
  • Options propagation: sets a styled numeric cell and verifies Options{RawCellValue: true} yields the unformatted raw value through IterateColumns exactly as it does
    through Columns.

Ran the existing TestRows, TestGetRows, and TestRowsIterator to confirm no regression on the shared iterate path.

Types of changes

  • Docs change / refactoring / dependency upgrade
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@codecov

codecov Bot commented May 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.60%. Comparing base (6bfbbee) to head (8b76038).
⚠️ Report is 12 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2330   +/-   ##
=======================================
  Coverage   99.60%   99.60%           
=======================================
  Files          32       32           
  Lines       26794    26807   +13     
=======================================
+ Hits        26688    26701   +13     
  Misses         55       55           
  Partials       51       51           
Flag Coverage Δ
unittests 99.60% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@xuri xuri added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support Rows.IterateColumns

2 participants