excelize icon indicating copy to clipboard operation
excelize copied to clipboard

Table data extraction

Open sides-flow opened this issue 1 year ago • 3 comments

  • Added method GetTableData to extract raw table values
  • Added method GetTableColumns to extract values with their columns

I'm not so sure about the naming of these functions, feel free to rename them however you want

PR Details

I needed to extract values from a table, and I found out it is hard for the user to use the GetTables to actually access the data (need to convert the range to coordinates and manually walk the cells). So I added a couple of methods to access the table's values with structured ways.

Description

Related Issue

Motivation and Context

How Has This Been Tested

I added the unit test for the methods I created. I ran all the tests locally.

Types of changes

  • [ ] Docs change / refactoring / dependency upgrade
  • [ ] Bug fix (non-breaking change which fixes an issue)
  • [x] 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.
  • [x] I have read the CONTRIBUTING document.
  • [x] I have added tests to cover my changes.
  • [x] All new and existing tests passed.

sides-flow avatar Aug 31 '23 12:08 sides-flow

Anything else needed to merge this in? @xuri

sides-flow avatar Jan 09 '24 11:01 sides-flow

Sorry for my late reply. Thanks for your pull request. Unfortunately, due to the principle of minimum availability, I didn't suggest introducing these functions to keep the library core and easy. Anyone can easily make a custom function to get cells in the table range by themselves: table cell value, table cell formula, table cell style, table cell conditional formatting rules, etc., The developers can implement that by the wrapper or composite the existing exported functions.

It cannot be implemented by the developer since there is no way to access the coordinates of the table by its range (in-order to iterate over the table's cells). I agree that it extends functionality of the library, but as of now I don't see a way of doing it with the current API (unless I copy code from the library to my code, which defeats the purpose)

sides-flow avatar Feb 02 '24 14:02 sides-flow

Excelize library provides a set of utility functions to convert between cell reference and column/row number, so the user can get table range, convert the range to coordinates, and walk the cells by simple code just like in this pull request. Users can get table cell value, table cell formula, table cell style, table cell conditional formatting rules, formula, etc. when walking the cells, so I do not suggest adding a series function for each scenario of the above: GetTableData, GetTableCellStyle, GetTableCellConditionalFormatting, GetTableCellFormula, etc., Here is a standalone example for get cell value in table range base on this pull request:

package main

import (
    "fmt"
    "math/rand"
    "strings"

    "github.com/xuri/excelize/v2"
)

func main() {
    f := excelize.NewFile()
    defer func() {
        if err := f.Close(); err != nil {
            fmt.Println(err)
        }
    }()
    // Generate random numbers and set cell values in the table range
    for rowNum := 2; rowNum <= 5; rowNum++ {
        row := make([]interface{}, 5)
        for colID := 0; colID < 5; colID++ {
            row[colID] = rand.Intn(100)
        }
        cell, err := excelize.CoordinatesToCellName(1, rowNum)
        if err != nil {
            fmt.Println(err)
            return
        }
        if err := f.SetSheetRow("Sheet1", cell, &row); err != nil {
            fmt.Println(err)
            return
        }
    }
    // Add a table
    if err := f.AddTable("Sheet1", &excelize.Table{
        Range:             "A1:E5",
        Name:              "table",
        StyleName:         "TableStyleMedium2",
        ShowFirstColumn:   true,
        ShowLastColumn:    true,
        ShowColumnStripes: true,
    }); err != nil {
        fmt.Println(err)
        return
    }
    tables, err := f.GetTables("Sheet1")
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, table := range tables {
        // Get table range, convert the range to coordinates
        tableRange := strings.Split(table.Range, ":")
        if len(tableRange) != 2 {
            return
        }
        fromCol, fromRow, err := excelize.CellNameToCoordinates(tableRange[0])
        if err != nil {
            fmt.Println(err)
            return
        }
        toCol, toRow, err := excelize.CellNameToCoordinates(tableRange[1])
        if err != nil {
            fmt.Println(err)
            return
        }
        // Walk the cells of the table
        for rowNum := fromRow; rowNum <= toRow; rowNum++ {
            for colNum := fromCol; colNum <= toCol; colNum++ {
                cell, err := excelize.CoordinatesToCellName(colNum, rowNum)
                if err != nil {
                    fmt.Println(err)
                    return
                }
                val, err := f.GetCellValue("Sheet1", cell)
                if err != nil {
                    fmt.Println(err)
                    return
                }
                fmt.Print(val, "\t")
            }
            fmt.Println()
        }
    }
    if err := f.SaveAs("Book1.xlsx"); err != nil {
        fmt.Println(err)
    }
}

xuri avatar Feb 04 '24 03:02 xuri