Browse Source

Resolve #382, rewrite prepareSheetXML to scale linearly (#383)

* Rewrite prepareSheetXML to scale linearly

We don't need to backfill columns into every row for most purposes
Provided makeContiguousColumns for setting styles where we do
need it for a specific region.

Added a benchmark to monitor progress. For 50,000 rows this went
from about 11 seconds to 1 second. The improvements are more
dramatic as the row/column count increases.

* Assigning that row value was redundant
Michael 6 years ago
parent
commit
0f9170a03b
4 changed files with 33 additions and 11 deletions
  1. 12 0
      cell_test.go
  2. 1 1
      rows.go
  3. 19 10
      sheet.go
  4. 1 0
      styles.go

+ 12 - 0
cell_test.go

@@ -82,3 +82,15 @@ func ExampleFile_SetCellFloat() {
 	fmt.Println(val)
 	// Output: 3.14
 }
+
+func BenchmarkSetCellValue(b *testing.B) {
+	values := []string{"First", "Second", "Third", "Fourth", "Fifth", "Sixth"}
+	cols := []string{"A", "B", "C", "D", "E", "F"}
+	f := NewFile()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		for j := 0; j < len(values); j++ {
+			f.SetCellValue("Sheet1", fmt.Sprint(cols[j], i), values[j])
+		}
+	}
+}

+ 1 - 1
rows.go

@@ -446,7 +446,7 @@ func (f *File) InsertRow(sheet string, row int) error {
 	return f.adjustHelper(sheet, rows, row, 1)
 }
 
-// DuplicateRow inserts a copy of specified row (by it Excel row number) below
+// DuplicateRow inserts a copy of specified row (by its Excel row number) below
 //
 //    err := xlsx.DuplicateRow("Sheet1", 2)
 //

+ 19 - 10
sheet.go

@@ -1072,8 +1072,8 @@ func (f *File) workSheetRelsWriter() {
 	}
 }
 
-// fillSheetData fill missing row and cell XML data to made it continuous from
-// first cell [1, 1] to last cell [col, row]
+// fillSheetData ensures there are enough rows, and columns in the chosen
+// row to accept data. Missing rows are backfilled and given their row number
 func prepareSheetXML(xlsx *xlsxWorksheet, col int, row int) {
 	rowCount := len(xlsx.SheetData.Row)
 	if rowCount < row {
@@ -1082,14 +1082,23 @@ func prepareSheetXML(xlsx *xlsxWorksheet, col int, row int) {
 			xlsx.SheetData.Row = append(xlsx.SheetData.Row, xlsxRow{R: rowIdx + 1})
 		}
 	}
-	for rowIdx := range xlsx.SheetData.Row {
-		rowData := &xlsx.SheetData.Row[rowIdx] // take reference
-		cellCount := len(rowData.C)
-		if cellCount < col {
-			for colIdx := cellCount; colIdx < col; colIdx++ {
-				cellName, _ := CoordinatesToCellName(colIdx+1, rowIdx+1)
-				rowData.C = append(rowData.C, xlsxC{R: cellName})
-			}
+	rowData := &xlsx.SheetData.Row[row-1]
+	fillColumns(rowData, col, row)
+}
+
+func fillColumns(rowData *xlsxRow, col, row int) {
+	cellCount := len(rowData.C)
+	if cellCount < col {
+		for colIdx := cellCount; colIdx < col; colIdx++ {
+			cellName, _ := CoordinatesToCellName(colIdx+1, row)
+			rowData.C = append(rowData.C, xlsxC{R: cellName})
 		}
 	}
 }
+
+func makeContiguousColumns(xlsx *xlsxWorksheet, fromRow, toRow, colCount int) {
+	for ; fromRow < toRow; fromRow++ {
+		rowData := &xlsx.SheetData.Row[fromRow-1]
+		fillColumns(rowData, colCount, fromRow)
+	}
+}

+ 1 - 0
styles.go

@@ -2373,6 +2373,7 @@ func (f *File) SetCellStyle(sheet, hcell, vcell string, styleID int) error {
 		return err
 	}
 	prepareSheetXML(xlsx, vcol, vrow)
+	makeContiguousColumns(xlsx, hrow, vrow, vcol)
 
 	for r := hrowIdx; r <= vrowIdx; r++ {
 		for k := hcolIdx; k <= vcolIdx; k++ {