Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Table data extraction #1639

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/xuri/excelize/v2
go 1.16

require (
github.com/google/go-cmp v0.5.9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/richardlehane/mscfb v1.0.4
github.com/richardlehane/msoleps v1.0.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
63 changes: 63 additions & 0 deletions table.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,69 @@ func (f *File) DeleteTable(name string) error {
return newNoExistTableError(name)
}

// GetTableData provides the method to return the values of a table given its range
// and worksheet name.
func (f *File) GetTableData(sheet string, tableRange string) ([][]string, error) {
coordinates, err := rangeRefToCoordinates(tableRange)
if err != nil {
return nil, fmt.Errorf("failed converting range '%v' to coordinates: %w", tableRange, err)
}
_ = sortCoordinates(coordinates)

var data [][]string
for row := coordinates[1]; row <= coordinates[3]; row++ {
var rowValues []string
for col := coordinates[0]; col <= coordinates[2]; col++ {
cell, _ := CoordinatesToCellName(col, row)
cellValue, err := f.GetCellValue(sheet, cell)
if err != nil {
return nil, fmt.Errorf("failed getting cell value of '%s': %w", cell, err)
}
rowValues = append(rowValues, cellValue)
}
data = append(data, rowValues)
}

return data, nil
}

// GetTableColumns provides the method to return the values of a table as a map with the headers as the keys,
// given its range and worksheet name.
func (f *File) GetTableColumns(sheet string, table Table) (map[string][]string, error) {
coordinates, err := rangeRefToCoordinates(table.Range)
if err != nil {
return nil, fmt.Errorf("failed converting range '%v' to coordinates: %w", table.Range, err)
}
_ = sortCoordinates(coordinates)

data := make(map[string][]string, coordinates[2]-coordinates[0]+1)
for col := coordinates[0]; col <= coordinates[2]; col++ {
header := "Column" + strconv.Itoa(col-coordinates[0]+1)
firstRow := coordinates[1]
if table.ShowHeaderRow != nil && *table.ShowHeaderRow {
cell, _ := CoordinatesToCellName(col, coordinates[1])
header, err = f.GetCellValue(sheet, cell)
if err != nil {
return nil, fmt.Errorf("failed getting header from '%s': %w", cell, err)
}
firstRow++
}

var rowValues []string
for row := firstRow; row <= coordinates[3]; row++ {
cell, _ := CoordinatesToCellName(col, row)
cellValue, err := f.GetCellValue(sheet, cell)
if err != nil {
return nil, fmt.Errorf("failed getting cell value of '%s': %w", cell, err)
}
rowValues = append(rowValues, cellValue)
}
data[header] = rowValues
}

return data, nil
}

// countTables provides a function to get table files count storage in the
// folder xl/tables.
func (f *File) countTables() int {
Expand Down
65 changes: 65 additions & 0 deletions table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"strings"
"testing"

"github.com/google/go-cmp/cmp"

"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -99,6 +101,69 @@ func TestGetTables(t *testing.T) {
assert.NoError(t, err)
}

func TestTableValuesExtraction(t *testing.T) {
sheetName := "Sheet1"
f := NewFile()

// Set headers
assert.NoError(t, f.SetCellValue(sheetName, "B3", "ID"))
assert.NoError(t, f.SetCellValue(sheetName, "C3", "Type"))
assert.NoError(t, f.SetCellValue(sheetName, "D3", "Name"))

// Set values
assert.NoError(t, f.SetCellValue(sheetName, "B4", 4))
assert.NoError(t, f.SetCellValue(sheetName, "C4", "Fire"))
assert.NoError(t, f.SetCellValue(sheetName, "D4", "Charmander"))
assert.NoError(t, f.SetCellValue(sheetName, "B5", 7))
assert.NoError(t, f.SetCellValue(sheetName, "C5", "Water"))
assert.NoError(t, f.SetCellValue(sheetName, "D5", "Squirtle"))
assert.NoError(t, f.SetCellValue(sheetName, "B6", 25))
assert.NoError(t, f.SetCellValue(sheetName, "C6", "Electric"))
assert.NoError(t, f.SetCellValue(sheetName, "D6", "Pikachu"))

// Add the table
pokemonTable := &Table{
Range: "B3:D6",
Name: "PokemonsTable",
ShowHeaderRow: boolPtr(true),
}
assert.NoError(t, f.AddTable(sheetName, pokemonTable))

// Test GetTableData
data, err := f.GetTableData(sheetName, pokemonTable.Range)
assert.NoError(t, err)
expectedData := [][]string{
{"ID", "Type", "Name"},
{"4", "Fire", "Charmander"},
{"7", "Water", "Squirtle"},
{"25", "Electric", "Pikachu"},
}
assert.True(t, cmp.Equal(data, expectedData), cmp.Diff(data, expectedData))

// Test GetTableColumns with headers
pokemonHeadersColumns, err := f.GetTableColumns(sheetName, *pokemonTable)
assert.NoError(t, err)
expectedHeadersData := map[string][]string{
"ID": {"4", "7", "25"},
"Type": {"Fire", "Water", "Electric"},
"Name": {"Charmander", "Squirtle", "Pikachu"},
}
assert.True(t, cmp.Equal(pokemonHeadersColumns, expectedHeadersData),
cmp.Diff(pokemonHeadersColumns, expectedHeadersData))

// Test GetTableColumns without headers
pokemonTable.ShowHeaderRow = boolPtr(false)
pokemonColumns, err := f.GetTableColumns(sheetName, *pokemonTable)
assert.NoError(t, err)
expectedColumnsData := map[string][]string{
"Column1": {"ID", "4", "7", "25"},
"Column2": {"Type", "Fire", "Water", "Electric"},
"Column3": {"Name", "Charmander", "Squirtle", "Pikachu"},
}
assert.True(t, cmp.Equal(pokemonColumns, expectedColumnsData),
cmp.Diff(pokemonColumns, expectedColumnsData))
}

func TestDeleteTable(t *testing.T) {
f := NewFile()
assert.NoError(t, f.AddTable("Sheet1", &Table{Range: "A1:B4", Name: "Table1"}))
Expand Down