Numeric value from xls file are not reading properly but the string values are fine
file, _ := xls.Open("test.xls", "utf-8")
sheet := file.GetSheet(0)
for r := 0; r <= (int(sheet.MaxRow)); r++ {
row := sheet.Row(r)
log.Println("column with numeric value: ", row.Col(0))
log.Println("column with string value: ", row.Col(1))
}
test.xls:
123 | test
456 | testing
output:
column with numeric value: #
column with string value: test
column with numeric value: #
column with string value: testing
How can I get numeric value correctly?
On my Ubuntu 18.04,I can open file and print content of second column
package main
import (
"fmt"
"github.com/extrame/xls"
"log"
)
func main() {
if xlFile, err := xls.Open("test.xls", "utf-8"); err == nil {
for i := 0; i < xlFile.NumSheets(); i++ {
sheet := xlFile.GetSheet(i)
fmt.Println(sheet.Name)
for r := 0; r <= (int(sheet.MaxRow)); r++ {
row := sheet.Row(r)
log.Println("column ", row.Col(1))
}
}
}
}
Pay special attention on Col(1) indexing.
Output
Sheet1
2019/04/03 14:28:29 column test
2019/04/03 14:28:29 column testi
2019/04/03 14:28:29 column testing
But for numerical column I got this
Sheet1
2019/04/03 14:27:46 column General
2019/04/03 14:27:46 column General
2019/04/03 14:27:46 column General
I saved the same file as xlsx. This works with tealeg/xlsx package
import (
"fmt"
"github.com/tealeg/xlsx"
)
func main() {
excelFileName := "test.xlsx"
xlFile, err := xlsx.OpenFile(excelFileName)
if err != nil {
err = fmt.Errorf("can not open file!!!", err)
return
}
for _, sheet := range xlFile.Sheets {
for _, row := range sheet.Rows {
for _, cell := range row.Cells {
text := cell.String()
fmt.Println(text)
}
}
}
}
Output
123
test
788
456
testi
999
789
testing
100
Related
How to sum an array of integers with mixed types of strings and numbers in GoLang?
Code below errors with "mismatched types int and any" and "cannot initialize 1 variables with 2 values".
Is there something like this JavaScript solution?
Function that sums array numbers (including numbers as strings)
errored code:
import (
"fmt"
"strconv"
)
func main() {
fmt.Println(sum([]any{9, 1, "8", "2"})) // this should output 20
}
func sum(arr []any) int {
n:=0
for _, v := range arr{
temp:=strconv.Atoi(v) //err: cannot initialize 1 variables with 2 values
n+=temp //err: mismatched types int and any
}
return n
}
This also errors:
n:=0
for _, v := range arr{
temp:=0
if reflect.TypeOf(v)=="string"{
temp=strconv.Atoi(v)
} else {
temp=v
}
n+=temp
}
return n
count+=temp //err: mismatched types int and any
Use a type switch to handle integer and string values as appropriate.
temp:=strconv.Atoi(v) //err: cannot initialize 1 variables with 2 values
strconv.Atoi returns two values. Assign the result to two variables. Handle the error return.
Here's the code with the fixes:
func sum(arr []any) int {
n := 0
for _, v := range arr {
switch v := v.(type) {
case int:
n += v
case string:
i, err := strconv.Atoi(v)
if err != nil {
panic(err)
}
n += i
default:
panic(fmt.Sprintf("unsupported type %T", v))
}
}
return n
}
For completeness, here's a version of the function that uses reflection. The type switch version of the function is preferred over reflection.
func sum(arr []any) int {
n := 0
for _, v := range arr {
v := reflect.ValueOf(v)
if v.Kind() == reflect.Int {
n += int(v.Int())
} else if v.Kind() == reflect.String {
i, err := strconv.Atoi(v.String())
if err != nil {
panic(err)
}
n += i
} else {
panic(fmt.Sprintf("unsupported type %s", v.Type()))
}
}
return n
}
Let say I have input and output string where the output will be the frequency of each elements in the string and the char itself
input := "programming"
output := "p2ro2ga2min"
How can I print it based on index after I found the freq of distinct character
This is my code
func countFreq(s string) {
sMap := make(map[string]int)
for _, v := range s {
sMap[string(v)]++
}
for i, v := range sMap {
fmt.Printf("%v%v", i, v)
}
// Don't know what to do next
}
The output of code is
output: n1p1r2o1g2a1m2i1
#icza's answer is great. Here's an alternative that I thought of before but only just got around to writing.
It uses a string to keep track of the rune order but you could use a string builder if speed is important.
func countFreq(s string) {
sMap := make(map[rune]int)
sOut := ""
for _, c := range s {
sMap[c]++
if sMap[c] == 1 {
sOut += string(c)
}
}
for _, c := range sOut {
if sMap[c] > 1 {
fmt.Print(sMap[c])
}
fmt.Printf("%c", c)
}
}
You're counting runes, so use a map of map[rune]int, so you can omit the conversions back to string.
Maps are unordered, so if you want the output in the same order as the input, you can't (shouldn't) iterate over the map.
Once you counted the letters, range over the input letters again, and get the frequency from the map, and remove it. If the count is greater than 1, also print the number.
func countFreq(s string) {
sMap := make(map[rune]int)
for _, v := range s {
sMap[v]++
}
for _, v := range s {
count := sMap[v]
if count == 0 {
continue // Char already printed and removed
}
delete(sMap, v)
if count > 1 {
fmt.Print(count)
}
fmt.Print(string(v))
}
}
Testing it:
for _, s := range []string{"programming", "ab", "aba", "aabcdeac"} {
fmt.Println("In:", s)
fmt.Print("Out: ")
countFreq(s)
fmt.Println()
}
This will output (try it on the Go Playground):
In: programming
Out: p2ro2ga2min
In: ab
Out: ab
In: aba
Out: 2ab
In: aabcdeac
Out: 3ab2cde
I was wondered how to loop over column cells of excel sheet in golang, here is my excel file:
I have tried this piece of code for other reason
package main
import (
"fmt"
"github.com/xuri/excelize/v2"
)
func main() {
f, err := excelize.OpenFile("pricematching.xlsx")
if err != nil {
fmt.Println(err)
return
}
// Get all the rows in the sheet1 section.
rows, err := f.GetCellValue("sheet1", "A2")
fmt.Print(rows, "\t")
}
No matter how's your excel file, this is the way to read each cell:
xlsxFile, error := excelize.OpenFile(filePath)
for _, sheetName := range xlsxFile.GetSheetMap() {
for rowIndex, rowValues := range xlsxFile.GetRows(sheetName) {
for columnIndex, columnValue := range rowValues {
// do what ever you want here
}
}
}
Not sure what exactly you need, but this is a simple way to get all cells in a column (if you know how many rows you want to read):
package main
import (
"fmt"
"github.com/xuri/excelize/v2"
)
func main() {
f, err := excelize.OpenFile("pricematching.xlsx")
if err != nil {
fmt.Println(err)
return
}
columnName := "A"
sheetName := "sheet1"
totalNumberOfRows := 20
for i := 1; i < totalNumberOfRows; i++ {
cellName := fmt.Sprintf("%s%d", columnName, i)
// fmt.Println(cellName)
cellValue, err := f.GetCellValue(sheetName, cellName)
fmt.Printf("%s\t", cellValue)
}
}
I use github.com/xuri/excelize/v2 to process the excel file.
I append many sheets in many excel files into one sheet in one excel.
Below is the sample code.
var mergedRows [][]string
for _, f := range files {
excelPath := folder + "/" + f.Name()
rows := loadXlsx(excelPath, sheetName)
for _, row := range rows[rowOffset:] {
mergedRows = append(mergedRows, row)
}
}
saveXlsx(aggregatedFilePath, sheetName, mergedRows, rowOffset)
...
func loadXlsx(xlsxPath string, sheetName string) [][]string {
f, err := excelize.OpenFile(xlsxPath)
if err != nil {
log.Fatal(err)
}
defer func() {
if err := f.Close(); err != nil{
fmt.Println(err)
}
}()
rows, err := f.GetRows(sheetName)
if err != nil {
log.Fatal(err)
}
return rows
}
func saveXlsx(path string, sheetName string, rows [][]string, rowOffset int) {
f, err := excelize.OpenFile(path)
if err != nil {
log.Fatal(err)
}
defer func() {
if err := f.Close(); err != nil{
fmt.Println(err)
}
}()
index := f.GetSheetIndex(sheetName)
offset := 1
sequence := 1
for _, row := range rows{
row[0] = strconv.Itoa(sequence)
sequence = sequence + 1
offset = offset + 1
axis := "A" + strconv.Itoa(offset)
f.SetSheetRow(sheetName, axis, &row)
}
for index, _ := range rows[0] {
axis, _ := excelize.CoordinatesToCellName(index, 2)
column, _ := excelize.ColumnNumberToName(index)
styleId, _ := f.GetCellStyle(sheetName, axis)
cellType, _ := f.GetCellType(sheetName, axis)
fmt.Println(styleId)
fmt.Println(cellType)
f.SetColStyle(sheetName, column, styleId)
}
f.SetActiveSheet(index)
if err := f.Save(); err != nil {
fmt.Println(err)
}
}
This works, except some data format issues. the number's style is copyed, but not works; the date is copyed, but with wrong value.
In the source file, there has some number with 2 decimal format and shows like 70.12, while in the output file the format is the same but shows like 70.119.
In the source file, there has some date with Y/m/d format and shows like 2022/1/12, while in the output file the format is the same but shows like 01-12-22.
From the manual
func (f *File) GetRows(sheet string, opts ...Options) ([][]string, error)
If the cell format can be applied to the value of the cell, the
applied value will be used, otherwise the original value will be used.
So in my question, rows, err := f.GetRows(sheetName) will copy the date and number value with format, not the original number. The formated value may be convert to non equal value.
The solution is just read the raw value with RawCellValue option true,
rows, err := f.GetRows(sheetName, excelize.Options{RawCellValue:true})
If the format is changed, just apply the style from the original file to the new file.
I want to expand a string of slice by delimiter "/".
For example, expanding the following slice
s := []string{"5/3","9","5/4/1","6"}
Should produce individual slices :
["5","9","5","6"] ["5","9","4","6"] ["5","9","1","6"]
["3","9","5","6"] ["3","9","4","6"] ["3","9","1","6"]
I am pretty much stuck here
var c [][]string{}
s := []string{"5/3","9","5/4/1","6"}
for _, v := range s {
combos := strings.Split(v, "/")
for _, combo := range combos {
}
}
Running time aside, you can achieve this with recursion.
func Perm(digits [][]string) (perm [][]string) {
if len(digits) == 0 || len(digits) == 1 {
return digits
}
nextDigits := Perm(digits[1:])
for _, digit := range digits[0] {
for _, next := range nextDigits {
cat := append([]string{digit}, next...)
perm = append(perm, cat)
}
}
return perm
}
Playground