Get all cells in a row with empty ones (#377)

* Get all cells in a row with empty ones
* sheet.MaxColumnIdx() changed
* goimports for all
This commit is contained in:
Vyacheslav Zgordan 2020-03-25 16:40:25 +03:00 committed by GitHub
parent 5f9c94bf84
commit 72ad869a28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 426 additions and 341 deletions

View File

@ -0,0 +1,36 @@
// Copyright 2017 FoxyUtils ehf. All rights reserved.
package main
// This example demonstrates outputing all cells in a row of an excel spreadsheet, including empty cells.
import (
"fmt"
"log"
"github.com/unidoc/unioffice/spreadsheet"
)
func main() {
ss, err := spreadsheet.Open("test.xlsx")
if err != nil {
log.Fatalf("error opening document: %s", err)
}
s := ss.Sheets()[0]
maxColumnIdx := s.MaxColumnIdx()
for _, row := range s.Rows() {
for _, cell := range row.CellsWithEmpty(maxColumnIdx) {
fmt.Println(cell.Reference(), ":", cell.GetFormattedValue())
}
}
fmt.Print("\n\n\n")
s.Cell("F4").SetString("Hello world")
maxColumnIdx = s.MaxColumnIdx()
for _, row := range s.Rows() {
for _, cell := range row.CellsWithEmpty(maxColumnIdx) {
fmt.Println(cell.Reference(), ":", cell.GetFormattedValue())
}
}
}

Binary file not shown.

View File

@ -10,9 +10,9 @@ import (
"github.com/unidoc/unioffice/common"
"github.com/unidoc/unioffice/document"
"github.com/unidoc/unioffice/schema/soo/ofc/docPropsVTypes"
"github.com/unidoc/unioffice/testhelper"
"github.com/unidoc/unioffice/zippkg"
"github.com/unidoc/unioffice/schema/soo/ofc/docPropsVTypes"
)
func TestMarshalCustomProperties(t *testing.T) {
@ -181,9 +181,9 @@ func ExampleCustomProperties() {
// And change them as well
cp.SetPropertyAsLpwstr("Another text property", "My text value") // text
cp.SetPropertyAsI4("Another integer number property", 42) // int23
cp.SetPropertyAsR8("Another float number property", 3.14) // float64
cp.SetPropertyAsDate("Another date property", time.Now()) // date
cp.SetPropertyAsI4("Another integer number property", 42) // int23
cp.SetPropertyAsR8("Another float number property", 3.14) // float64
cp.SetPropertyAsDate("Another date property", time.Now()) // date
doc.SaveToFile("document.docx")
}

View File

@ -395,7 +395,6 @@ func (p *Presentation) Save(w io.Writer) error {
r.Properties().SetSolidFill(color.Red)
}
dt := unioffice.DocTypePresentation
z := zip.NewWriter(w)

View File

@ -8,6 +8,7 @@
// commercial license can be purchased via https://unidoc.io website.
package custom_properties
import "github.com/unidoc/unioffice"
// init registers constructor functions for dynamically creating elements based off the XML namespace and name

View File

@ -26,10 +26,10 @@ const iso8601Format = "2006-01-02T15:04:05Z07:00"
// Cell is a single cell within a sheet.
type Cell struct {
w *Workbook
s *sml.Worksheet
r *sml.CT_Row
x *sml.CT_Cell
w *Workbook
sheet *Sheet
r *sml.CT_Row
x *sml.CT_Cell
}
// X returns the inner wrapped XML type.
@ -110,8 +110,8 @@ func (c Cell) SetFormulaShared(formula string, rows, cols uint32) error {
}
sid := uint32(0)
for _, r := range c.s.SheetData.Row {
for _, c := range r.C {
for _, r := range c.sheet.Rows() {
for _, c := range r.x.C {
if c.F != nil && c.F.SiAttr != nil && *c.F.SiAttr >= sid {
sid = *c.F.SiAttr
}
@ -122,7 +122,7 @@ func (c Cell) SetFormulaShared(formula string, rows, cols uint32) error {
ref := fmt.Sprintf("%s%d:%s%d", cref.Column, cref.RowIdx, reference.IndexToColumn(cref.ColumnIdx+cols), cref.RowIdx+rows)
c.x.F.RefAttr = unioffice.String(ref)
c.x.F.SiAttr = unioffice.Uint32(sid)
sheet := Sheet{c.w, nil, c.s}
sheet := Sheet{c.w, c.sheet.cts, c.sheet.x}
for row := cref.RowIdx; row <= cref.RowIdx+rows; row++ {
for col := cref.ColumnIdx; col <= cref.ColumnIdx+cols; col++ {
if row == cref.RowIdx && col == cref.ColumnIdx {
@ -200,11 +200,16 @@ func (c Cell) getLabelPrefix() string {
sid := *c.x.SAttr
cs := c.w.StyleSheet.GetCellStyle(sid)
switch cs.xf.Alignment.HorizontalAttr {
case sml.ST_HorizontalAlignmentLeft: return "'"
case sml.ST_HorizontalAlignmentRight: return "\""
case sml.ST_HorizontalAlignmentCenter: return "^"
case sml.ST_HorizontalAlignmentFill: return "\\"
default: return ""
case sml.ST_HorizontalAlignmentLeft:
return "'"
case sml.ST_HorizontalAlignmentRight:
return "\""
case sml.ST_HorizontalAlignmentCenter:
return "^"
case sml.ST_HorizontalAlignmentFill:
return "\\"
default:
return ""
}
}
@ -504,22 +509,23 @@ func (c Cell) GetRawValue() (string, error) {
// SetHyperlink sets a hyperlink on a cell.
func (c Cell) SetHyperlink(hl common.Hyperlink) {
if c.s.Hyperlinks == nil {
c.s.Hyperlinks = sml.NewCT_Hyperlinks()
ws := c.sheet.x
if ws.Hyperlinks == nil {
ws.Hyperlinks = sml.NewCT_Hyperlinks()
}
rel := common.Relationship(hl)
hle := sml.NewCT_Hyperlink()
hle.RefAttr = c.Reference()
hle.IdAttr = unioffice.String(rel.ID())
c.s.Hyperlinks.Hyperlink = append(c.s.Hyperlinks.Hyperlink, hle)
ws.Hyperlinks.Hyperlink = append(ws.Hyperlinks.Hyperlink, hle)
}
// AddHyperlink creates and sets a hyperlink on a cell.
func (c Cell) AddHyperlink(url string) {
// store the relationships so we don't need to do a lookup here?
for i, ws := range c.w.xws {
if ws == c.s {
if ws == c.sheet.x {
// add a hyperlink relationship in the worksheet relationships file
c.SetHyperlink(c.w.xwsRels[i].AddHyperlink(url))
return

View File

@ -9,10 +9,10 @@ package spreadsheet
import (
"fmt"
"time"
"regexp"
"strconv"
"strings"
"time"
"github.com/unidoc/unioffice/spreadsheet/formula"
"github.com/unidoc/unioffice/spreadsheet/reference"
@ -210,7 +210,7 @@ func (e *evalContext) LastColumn(rowFrom, rowTo int) string {
max = l
}
}
return reference.IndexToColumn(uint32(max-1))
return reference.IndexToColumn(uint32(max - 1))
}
// LastRow returns the name of last row which contains data in range of context sheet's given columns.
@ -220,7 +220,7 @@ func (e *evalContext) LastRow(col string) int {
max := 1
for _, r := range sheet.x.SheetData.Row {
if r.RAttr != nil {
row := Row{sheet.w, sheet.x, r}
row := Row{sheet.w, sheet, r}
l := len(row.Cells())
if l > colIdx {
max = int(row.RowNumber())

View File

@ -410,30 +410,30 @@ func listValueOp(op BinOpType, lhs []Result, rhs Result) Result {
func (b BinaryExpr) String() string {
opStr := ""
switch b.op {
case BinOpTypePlus:
opStr = "+"
case BinOpTypeMinus:
opStr = "-"
case BinOpTypeMult:
opStr = "*"
case BinOpTypeDiv:
opStr = "/"
case BinOpTypeExp:
opStr = "^"
case BinOpTypeLT:
opStr = "<"
case BinOpTypeGT:
opStr = ">"
case BinOpTypeEQ:
opStr = "="
case BinOpTypeLEQ:
opStr = "<="
case BinOpTypeGEQ:
opStr = ">="
case BinOpTypeNE:
opStr = "<>"
case BinOpTypeConcat:
opStr = "&"
case BinOpTypePlus:
opStr = "+"
case BinOpTypeMinus:
opStr = "-"
case BinOpTypeMult:
opStr = "*"
case BinOpTypeDiv:
opStr = "/"
case BinOpTypeExp:
opStr = "^"
case BinOpTypeLT:
opStr = "<"
case BinOpTypeGT:
opStr = ">"
case BinOpTypeEQ:
opStr = "="
case BinOpTypeLEQ:
opStr = "<="
case BinOpTypeGEQ:
opStr = ">="
case BinOpTypeNE:
opStr = "<>"
case BinOpTypeConcat:
opStr = "&"
}
return b.lhs.String() + opStr + b.rhs.String()
}

View File

@ -11,8 +11,8 @@ import "sync"
// evCache is a struct with collection of caching methods intended for add cache support to evaluators.
type evCache struct {
cache map[string]Result
lock *sync.Mutex
cache map[string]Result
lock *sync.Mutex
}
func newEvCache() evCache {

View File

@ -40,29 +40,29 @@ var daysTo1970 float64 = 25569.0
var daysInMonth = []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
var month2num = map[string]int{
"january": 1,
"february": 2,
"march": 3,
"april": 4,
"may": 5,
"june": 6,
"july": 7,
"august": 8,
"january": 1,
"february": 2,
"march": 3,
"april": 4,
"may": 5,
"june": 6,
"july": 7,
"august": 8,
"septemper": 9,
"october": 10,
"november": 11,
"december": 12,
"jan": 1,
"feb": 2,
"mar": 3,
"apr": 4,
"jun": 6,
"jul": 7,
"aug": 8,
"sep": 9,
"oct": 10,
"nov": 11,
"dec": 12,
"october": 10,
"november": 11,
"december": 12,
"jan": 1,
"feb": 2,
"mar": 3,
"apr": 4,
"jun": 6,
"jul": 7,
"aug": 8,
"sep": 9,
"oct": 10,
"nov": 11,
"dec": 12,
}
var dateFormats = map[string]*regexp.Regexp{}
@ -208,8 +208,8 @@ func dateFromDays(days float64) time.Time {
return time.Unix(0, unix)
}
func daysFromDate(y,m,d int) float64 {
return float64(makeDateS(y, time.Month(m), d) / 86400) + daysTo1970
func daysFromDate(y, m, d int) float64 {
return float64(makeDateS(y, time.Month(m), d)/86400) + daysTo1970
}
// DateDif is an implementation of the Excel DATEDIF() function.
@ -588,7 +588,7 @@ func Now(args []Result) Result {
}
now := time.Now()
_, offset := now.Zone()
nowS := daysTo1970 + float64(now.Unix() + int64(offset))/86400
nowS := daysTo1970 + float64(now.Unix()+int64(offset))/86400
return MakeNumberResult(nowS)
}
@ -599,12 +599,12 @@ func Today(args []Result) Result {
}
now := time.Now()
_, offset := now.Zone()
nowS := daysBetween(date1900, now.Unix() + int64(offset)) + 1
nowS := daysBetween(date1900, now.Unix()+int64(offset)) + 1
return MakeNumberResult(nowS)
}
func daysFromTime(hours, minutes, seconds float64) float64 {
return (hours * 3600 + minutes * 60 + seconds) / 86400
return (hours*3600 + minutes*60 + seconds) / 86400
}
// Time is an implementation of the Excel TIME() function.
@ -819,17 +819,17 @@ func yearFrac(startDateF, endDateF float64, basis int) (float64, Result) {
ed = 30
}
}
dayDiff = float64((ey - sy) * 360 + (em - sm) * 30 + (ed - sd))
dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))
daysInYear = 360
case 1:
dayDiff = endDateF - startDateF
isYearDifferent := sy != ey
if isYearDifferent && (ey != sy + 1 || sm < em || (sm == em && sd < ed)) {
if isYearDifferent && (ey != sy+1 || sm < em || (sm == em && sd < ed)) {
dayCount := 0
for y := sy; y <= ey; y++ {
dayCount += getDaysInYear(y, 1)
}
daysInYear = float64(dayCount) / float64(ey - sy + 1)
daysInYear = float64(dayCount) / float64(ey-sy+1)
} else {
if !isYearDifferent && isLeapYear(sy) {
daysInYear = 366
@ -847,14 +847,14 @@ func yearFrac(startDateF, endDateF float64, basis int) (float64, Result) {
case 3:
dayDiff = endDateF - startDateF
daysInYear = 365
case 4:
case 4:
if sd == 31 {
sd--
}
if ed == 31 {
ed--
}
dayDiff = float64((ey - sy) * 360 + (em - sm) * 30 + (ed - sd))
dayDiff = float64((ey-sy)*360 + (em-sm)*30 + (ed - sd))
daysInYear = 360
default:
return 0, MakeErrorResultType(ErrorTypeNum, "Incorrect basis for YearFrac")
@ -896,7 +896,7 @@ func isLeapYear(year int) bool {
}
func daysBetween(startDate, endDate int64) float64 {
return float64(int(0.5 + float64((endDate - startDate) / 86400)))
return float64(int(0.5 + float64((endDate-startDate)/86400)))
}
func feb29Between(date1, date2 time.Time) bool {
@ -904,7 +904,7 @@ func feb29Between(date1, date2 time.Time) bool {
date2S := date2.Unix()
year1 := date1.Year()
mar1year1 := makeDateS(year1, time.March, 1)
if (isLeapYear(year1) && date1S < mar1year1 && date2S >= mar1year1) {
if isLeapYear(year1) && date1S < mar1year1 && date2S >= mar1year1 {
return true
}
var year2 = date2.Year()
@ -929,7 +929,7 @@ func getDiff(from, to time.Time, basis int) float64 {
}
if basis == 0 {
if (mFrom == 2 || dFrom < 30 ) && dToOrig == 31 {
if (mFrom == 2 || dFrom < 30) && dToOrig == 31 {
dTo = 31
} else if mTo == 2 && dTo == getDaysInMonth(yTo, mTo) {
dTo = getDaysInMonth(yTo, 2)
@ -947,14 +947,14 @@ func getDiff(from, to time.Time, basis int) float64 {
diff = 30 - dFrom + 1
dFromOrig = 1
dFrom = 1
fromNew := time.Date(yFrom, time.Month(mFrom), dFromOrig, 0, 0, 0, 0, time.UTC).AddDate(0,1,0)
fromNew := time.Date(yFrom, time.Month(mFrom), dFromOrig, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0)
if fromNew.Year() < yTo {
diff += getDaysInMonthRange(fromNew.Year(), int(fromNew.Month()), 12, basis)
fromNew = fromNew.AddDate(0, 13 - int(fromNew.Month()), 0)
diff += getDaysInYearRange(fromNew.Year(), yTo - 1, basis)
fromNew = fromNew.AddDate(0, 13-int(fromNew.Month()), 0)
diff += getDaysInYearRange(fromNew.Year(), yTo-1, basis)
}
diff += getDaysInMonthRange(yTo, int(fromNew.Month()), mTo - 1, basis)
fromNew = fromNew.AddDate(0, mTo - int(fromNew.Month()), 0)
diff += getDaysInMonthRange(yTo, int(fromNew.Month()), mTo-1, basis)
fromNew = fromNew.AddDate(0, mTo-int(fromNew.Month()), 0)
mFrom = fromNew.Day()
}
diff += dTo - dFrom
@ -1028,7 +1028,7 @@ func parseDate(arg Result, dateName, funcName string) (float64, Result) {
return 0, MakeErrorResult("Incorrect argument for " + funcName)
}
if date < 0 {
return 0, MakeErrorResultType(ErrorTypeNum, dateName + " should be non negative")
return 0, MakeErrorResultType(ErrorTypeNum, dateName+" should be non negative")
}
return date, empty
}

View File

@ -8,10 +8,10 @@
package formula
import (
"time"
"math"
"strconv"
"strings"
"time"
)
func init() {
@ -81,16 +81,16 @@ func getSettlementMaturity(settlementResult, maturityResult Result, funcName str
return 0, 0, errResult
}
if settlementDate >= maturityDate {
return 0, 0, MakeErrorResultType(ErrorTypeNum, funcName + " requires maturity date to be later than settlement date")
return 0, 0, MakeErrorResultType(ErrorTypeNum, funcName+" requires maturity date to be later than settlement date")
}
return settlementDate, maturityDate, empty
}
type couponArgs struct {
settlementDate float64
maturityDate float64
freq int
basis int
maturityDate float64
freq int
basis int
}
// Coupdaybs implements the Excel COUPDAYBS function.
@ -125,7 +125,7 @@ func coupdays(settlementDateF, maturityDateF float64, freq, basis int) float64 {
maturityDate := dateFromDays(maturityDateF)
if basis == 1 {
pcd := couppcd(settlementDate, maturityDate, freq, 1)
next := pcd.AddDate(0, 12 / freq, 0)
next := pcd.AddDate(0, 12/freq, 0)
return getDiff(pcd, next, basis)
}
return float64(getDaysInYear(0, basis)) / float64(freq)
@ -199,7 +199,7 @@ func coupncd(settlementDate, maturityDate time.Time, freq int) time.Time {
ncd = ncd.AddDate(-1, 0, 0)
}
for !ncd.After(settlementDate) {
ncd = ncd.AddDate(0, 12 / freq, 0)
ncd = ncd.AddDate(0, 12/freq, 0)
}
return ncd
}
@ -227,7 +227,7 @@ func parseCouponArgs(args []Result, funcName string) (*couponArgs, Result) {
}
basis = int(args[3].ValueNumber)
if !checkBasis(basis) {
return nil, MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for " + funcName)
return nil, MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for "+funcName)
}
}
return &couponArgs{
@ -258,8 +258,8 @@ func coupnum(settlementDateF, maturityDateF float64, freq, basis int) (float64,
settlementDate, maturityDate := dateFromDays(settlementDateF), dateFromDays(maturityDateF)
if maturityDate.After(settlementDate) {
aDate := couppcd(settlementDate, maturityDate, freq, basis)
months := (maturityDate.Year() - aDate.Year()) * 12 + int(maturityDate.Month()) - int(aDate.Month())
return float64(months * freq) / 12.0, empty
months := (maturityDate.Year()-aDate.Year())*12 + int(maturityDate.Month()) - int(aDate.Month())
return float64(months*freq) / 12.0, empty
}
return 0, MakeErrorResultType(ErrorTypeNum, "Settlement date should be before maturity date")
}
@ -279,7 +279,7 @@ func getDuration(settlementDate, maturityDate, coup, yield, freq float64, basis
coup *= 100 / freq
yield /= freq
yield++
diff := frac * freq - coups
diff := frac*freq - coups
for t := 1.0; t < coups; t++ {
tDiff := t + diff
add := coup / math.Pow(yield, tDiff)
@ -287,7 +287,7 @@ func getDuration(settlementDate, maturityDate, coup, yield, freq float64, basis
duration += tDiff * add
}
add := (coup + 100) / math.Pow(yield, coups + diff)
add := (coup + 100) / math.Pow(yield, coups+diff)
p += add
duration += (coups + diff) * add
@ -300,11 +300,11 @@ func getDuration(settlementDate, maturityDate, coup, yield, freq float64, basis
type durationArgs struct {
settlementDate float64
maturityDate float64
coupon float64
yield float64
freq float64
basis int
maturityDate float64
coupon float64
yield float64
freq float64
basis int
}
// validateDurationData returns settlement date, maturity date, coupon rate, yield rate, frequency of payments, day count basis and error result by parsing incoming arguments
@ -349,7 +349,7 @@ func parseDurationData(args []Result, funcName string) (*durationArgs, Result) {
}
basis = int(basisResult.ValueNumber)
if !checkBasis(basis) {
return nil, MakeErrorResultType(ErrorTypeNum, "Incorrect basis value for " + funcName)
return nil, MakeErrorResultType(ErrorTypeNum, "Incorrect basis value for "+funcName)
}
}
return &durationArgs{
@ -501,21 +501,21 @@ func Amorlinc(args []Result) Result {
if period <= numOfFullPeriods {
return MakeNumberResult(oneRate)
} else if period == numOfFullPeriods + 1 {
return MakeNumberResult(costDelta - oneRate * float64(numOfFullPeriods) - r0)
} else if period == numOfFullPeriods+1 {
return MakeNumberResult(costDelta - oneRate*float64(numOfFullPeriods) - r0)
} else {
return MakeNumberResult(0)
}
}
type amorArgs struct {
cost float64
cost float64
datePurchased float64
firstPeriod float64
salvage float64
period int
rate float64
basis int
firstPeriod float64
salvage float64
period int
rate float64
basis int
}
func parseAmorArgs(args []Result, funcName string) (*amorArgs, Result) {
@ -528,7 +528,7 @@ func parseAmorArgs(args []Result, funcName string) (*amorArgs, Result) {
}
cost := args[0].ValueNumber
if cost < 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires cost to be non negative")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires cost to be non negative")
}
datePurchased, errResult := parseDate(args[1], "date purchased", funcName)
if errResult.Type == ResultTypeError {
@ -539,28 +539,28 @@ func parseAmorArgs(args []Result, funcName string) (*amorArgs, Result) {
return nil, errResult
}
if firstPeriod < datePurchased {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires first period to be later than date purchased")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires first period to be later than date purchased")
}
if args[3].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires salvage to be number argument")
}
salvage := args[3].ValueNumber
if salvage < 0 || salvage > cost {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires salvage to be between 0 and the initial cost")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires salvage to be between 0 and the initial cost")
}
if args[4].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires period to be number argument")
}
period := int(args[4].ValueNumber)
if period < 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires period to be non-negative")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires period to be non-negative")
}
if args[5].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires depreciation rate to be number argument")
}
rate := args[5].ValueNumber
if rate < 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires depreciation rate to be more than 0 and less than 0.5")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires depreciation rate to be more than 0 and less than 0.5")
}
basis := 0
if argsNum == 7 && args[6].Type != ResultTypeEmpty {
@ -569,7 +569,7 @@ func parseAmorArgs(args []Result, funcName string) (*amorArgs, Result) {
}
basis = int(args[6].ValueNumber)
if !checkBasis(basis) || basis == 2 {
return nil, MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for " + funcName)
return nil, MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for "+funcName)
}
}
return &amorArgs{
@ -588,12 +588,12 @@ func mathRound(x float64) float64 {
}
type cumulArgs struct {
rate float64
nPer float64
rate float64
nPer float64
presentValue float64
startPeriod float64
endPeriod float64
t int
startPeriod float64
endPeriod float64
t int
}
// Cumipmt implements the Excel CUMIPMT function.
@ -619,9 +619,9 @@ func Cumipmt(args []Result) Result {
}
for i := startPeriod; i <= endPeriod; i++ {
if t == 1 {
interest += fv(rate, i - 2, payment, presentValue, 1) - payment
interest += fv(rate, i-2, payment, presentValue, 1) - payment
} else {
interest += fv(rate, i - 1, payment, presentValue, 0)
interest += fv(rate, i-1, payment, presentValue, 0)
}
}
interest *= rate
@ -645,7 +645,7 @@ func Cumprinc(args []Result) Result {
principal := 0.0
if startPeriod == 1 {
if t == 0 {
principal = payment + presentValue * rate
principal = payment + presentValue*rate
} else {
principal = payment
}
@ -653,9 +653,9 @@ func Cumprinc(args []Result) Result {
}
for i := startPeriod; i <= endPeriod; i++ {
if t == 1 {
principal += payment - (fv(rate, i - 2, payment, presentValue, 1) - payment) * rate
principal += payment - (fv(rate, i-2, payment, presentValue, 1)-payment)*rate
} else {
principal += payment - fv(rate, i - 1, payment, presentValue, 0) * rate
principal += payment - fv(rate, i-1, payment, presentValue, 0)*rate
}
}
return MakeNumberResult(principal)
@ -670,45 +670,45 @@ func parseCumulArgs(args []Result, funcName string) (*cumulArgs, Result) {
}
rate := args[0].ValueNumber
if rate <= 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires rate to be positive number argument")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires rate to be positive number argument")
}
if args[1].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires number of periods to be number argument")
}
nPer := args[1].ValueNumber
if nPer <= 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires number of periods to be positive number argument")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires number of periods to be positive number argument")
}
if args[2].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires present value to be number argument")
}
presentValue := args[2].ValueNumber
if presentValue <= 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires present value to be positive number argument")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires present value to be positive number argument")
}
if args[3].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires start period to be number argument")
}
startPeriod := args[3].ValueNumber
if startPeriod <= 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires start period to be positive number argument")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires start period to be positive number argument")
}
if args[4].Type != ResultTypeNumber {
return nil, MakeErrorResult(funcName + " requires end period to be number argument")
}
endPeriod := args[4].ValueNumber
if endPeriod <= 0 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires end period to be positive number argument")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires end period to be positive number argument")
}
if endPeriod < startPeriod {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires end period to be later or equal to start period")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires end period to be later or equal to start period")
}
if endPeriod > nPer {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires periods to be in periods range")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires periods to be in periods range")
}
t := int(args[5].ValueNumber)
if t != 0 && t != 1 {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires type to be 0 or 1")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires type to be 0 or 1")
}
return &cumulArgs{
rate,
@ -720,16 +720,16 @@ func parseCumulArgs(args []Result, funcName string) (*cumulArgs, Result) {
}, empty
}
func pmt(rate, periods, present, future float64, t int ) float64 {
func pmt(rate, periods, present, future float64, t int) float64 {
var result float64
if rate == 0 {
result = (present + future) / periods
} else {
term := math.Pow(1 + rate, periods)
term := math.Pow(1+rate, periods)
if t == 1 {
result = (future * rate / (term - 1) + present * rate / (1 - 1 / term)) / (1 + rate)
result = (future*rate/(term-1) + present*rate/(1-1/term)) / (1 + rate)
} else {
result = future * rate / (term - 1) + present * rate / (1 - 1 / term)
result = future*rate/(term-1) + present*rate/(1-1/term)
}
}
return -result
@ -738,13 +738,13 @@ func pmt(rate, periods, present, future float64, t int ) float64 {
func fv(rate, periods, payment, value float64, t int) float64 {
var result float64
if rate == 0 {
result = value + payment * periods
result = value + payment*periods
} else {
term := math.Pow(1 + rate, periods)
term := math.Pow(1+rate, periods)
if t == 1 {
result = value * term + payment * (1 + rate) * (term - 1) / rate
result = value*term + payment*(1+rate)*(term-1)/rate
} else {
result = value * term + payment * (term - 1) / rate
result = value*term + payment*(term-1)/rate
}
}
return -result
@ -784,7 +784,7 @@ func Db(args []Result) Result {
if period <= 0 {
return MakeErrorResultType(ErrorTypeNum, "DB requires period to be positive")
}
if period - life > 1 {
if period-life > 1 {
return MakeErrorResultType(ErrorTypeNum, "Incorrect period for DB")
}
month := 12.0
@ -803,8 +803,8 @@ func Db(args []Result) Result {
if salvage >= cost {
return MakeNumberResult(0)
}
rate := 1 - math.Pow(salvage / cost, 1 / life)
rate = float64(int(rate * 1000 + 0.5)) / 1000 // round to 3 decimal places
rate := 1 - math.Pow(salvage/cost, 1/life)
rate = float64(int(rate*1000+0.5)) / 1000 // round to 3 decimal places
initial := cost * rate * month / 12
if period == 1 {
return MakeNumberResult(initial)
@ -945,7 +945,7 @@ func Dollarde(args []Result) Result {
return MakeErrorResult("Incorrect fraction argument for DOLLARDE")
}
dollarFrac *= math.Pow(10, power)
dollarde := dollarInt + dollarFrac / fraction
dollarde := dollarInt + dollarFrac/fraction
if neg {
dollarde = -dollarde
}
@ -976,7 +976,7 @@ func Dollarfr(args []Result) Result {
dollarFracOrder := float64(len(dollarFracStr))
dollarFrac /= math.Pow(10, dollarFracOrder)
dollarfr := dollarFrac * fraction / math.Pow(10, float64(int(math.Log10(fraction))) + 1) + dollarInt
dollarfr := dollarFrac*fraction/math.Pow(10, float64(int(math.Log10(fraction)))+1) + dollarInt
if neg {
dollarfr = -dollarfr
}
@ -996,7 +996,7 @@ func parseDollarArgs(args []Result, funcName string) (float64, float64, Result)
}
fraction := float64(int(args[1].ValueNumber))
if fraction < 0 {
return 0, 0, MakeErrorResultType(ErrorTypeNum, funcName + " requires fraction to be non negative number")
return 0, 0, MakeErrorResultType(ErrorTypeNum, funcName+" requires fraction to be non negative number")
}
return dollar, fraction, empty
}
@ -1036,7 +1036,7 @@ func Effect(args []Result) Result {
if npery < 1 {
return MakeErrorResultType(ErrorTypeNum, "EFFECT requires number of compounding periods to be 1 or more")
}
return MakeNumberResult(math.Pow((1 + nominal / npery), npery) - 1)
return MakeNumberResult(math.Pow((1+nominal/npery), npery) - 1)
}
// Fv implements the Excel FV function.
@ -1091,7 +1091,7 @@ func Fvschedule(args []Result) Result {
principal := args[0].ValueNumber
switch args[1].Type {
case ResultTypeNumber:
return MakeNumberResult(principal * (args[1].ValueNumber+1))
return MakeNumberResult(principal * (args[1].ValueNumber + 1))
case ResultTypeList, ResultTypeArray:
schedule := arrayFromRange(args[1])
for _, row := range schedule {
@ -1209,9 +1209,9 @@ func ipmt(rate, period, nPer, presentValue, futureValue float64, t int) float64
}
} else {
if t == 1 {
interest = fv(rate, period - 2, payment, presentValue, 1) - payment
interest = fv(rate, period-2, payment, presentValue, 1) - payment
} else {
interest = fv(rate, period - 1, payment, presentValue, 0)
interest = fv(rate, period-1, payment, presentValue, 0)
}
}
return interest * rate
@ -1256,7 +1256,7 @@ func Irr(args []Result) Result {
if i == 0 {
dates = append(dates, 0)
} else {
dates = append(dates, dates[i - 1] + 365)
dates = append(dates, dates[i-1]+365)
}
}
return irr(values, dates, guess)
@ -1289,7 +1289,7 @@ func irr(values, dates []float64, guess float64) Result {
for {
resultValue := irrResult(values, dates, resultRate)
newRate := resultRate - resultValue / irrResultDeriv(values, dates, resultRate)
newRate := resultRate - resultValue/irrResultDeriv(values, dates, resultRate)
epsRate := math.Abs(newRate - resultRate)
resultRate = newRate
iter++
@ -1313,7 +1313,7 @@ func irrResult(values, dates []float64, rate float64) float64 {
vlen := len(values)
firstDate := dates[0]
for i := 1; i < vlen; i++ {
result += values[i] / math.Pow(r, (dates[i] - firstDate) / 365)
result += values[i] / math.Pow(r, (dates[i]-firstDate)/365)
}
return result
}
@ -1325,7 +1325,7 @@ func irrResultDeriv(values, dates []float64, rate float64) float64 {
firstDate := dates[0]
for i := 1; i < vlen; i++ {
frac := (dates[i] - firstDate) / 365
result -= frac * values[i] / math.Pow(r, frac + 1)
result -= frac * values[i] / math.Pow(r, frac+1)
}
return result
}
@ -1355,7 +1355,7 @@ func Ispmt(args []Result) Result {
}
presentValue := args[3].ValueNumber
return MakeNumberResult(presentValue * rate * (period / nPer - 1))
return MakeNumberResult(presentValue * rate * (period/nPer - 1))
}
// Mduration implements the Excel MDURATION function.
@ -1375,7 +1375,7 @@ func Mduration(args []Result) Result {
if duration.Type == ResultTypeError {
return duration
}
mDuration := duration.ValueNumber / (1.0 + yield / freq)
mDuration := duration.ValueNumber / (1.0 + yield/freq)
return MakeNumberResult(mDuration)
}
@ -1430,8 +1430,8 @@ func Mirr(args []Result) Result {
}
result := -npvReinvest / npvInvest
result *= math.Pow(reinvRate, n - 1)
result = math.Pow(result, 1 / (n - 1))
result *= math.Pow(reinvRate, n-1)
result = math.Pow(result, 1/(n-1))
return MakeNumberResult(result - 1)
}
@ -1454,7 +1454,7 @@ func Nominal(args []Result) Result {
if npery < 1 {
return MakeErrorResultType(ErrorTypeNum, "NOMINAL requires number of compounding periods to be 1 or more")
}
return MakeNumberResult((math.Pow(effect + 1, 1 / npery) - 1) * npery)
return MakeNumberResult((math.Pow(effect+1, 1/npery) - 1) * npery)
}
// Nper implements the Excel NPER function.
@ -1492,9 +1492,9 @@ func Nper(args []Result) Result {
t = 1
}
}
num := pmt * (1 + rate * t) - futureValue * rate
den := (presentValue * rate + pmt * (1 + rate * t))
return MakeNumberResult(math.Log(num / den) / math.Log(1 + rate))
num := pmt*(1+rate*t) - futureValue*rate
den := (presentValue*rate + pmt*(1+rate*t))
return MakeNumberResult(math.Log(num/den) / math.Log(1+rate))
}
// Npv implements the Excel NPV function.
@ -1528,7 +1528,7 @@ func Npv(args []Result) Result {
}
npv := 0.0
for i, value := range values {
npv += value / math.Pow(1 + rate, float64(i) + 1)
npv += value / math.Pow(1+rate, float64(i)+1)
}
return MakeNumberResult(npv)
}
@ -1582,7 +1582,7 @@ func Oddlprice(args []Result) Result {
return MakeErrorResultType(ErrorTypeNum, "Incorrect frequence value")
}
basis := 0
if len(args) == 8 && args[7].Type != ResultTypeEmpty {
if len(args) == 8 && args[7].Type != ResultTypeEmpty {
basisResult := args[7]
if basisResult.Type != ResultTypeNumber {
return MakeErrorResult("ODDLPRICE requires basis of type number")
@ -1609,8 +1609,8 @@ func Oddlprice(args []Result) Result {
}
a *= freq
p := redemption + dc * 100 * rate / freq
p /= dsc * yield / freq + 1
p := redemption + dc*100*rate/freq
p /= dsc*yield/freq + 1
p -= a * 100 * rate / freq
return MakeNumberResult(p)
@ -1687,8 +1687,8 @@ func Oddlyield(args []Result) Result {
}
a *= freq
yield := redemption + dc * 100 * rate / freq
yield /= pr + a * 100 * rate / freq
yield := redemption + dc*100*rate/freq
yield /= pr + a*100*rate/freq
yield--
yield *= freq / dsc
@ -1721,7 +1721,7 @@ func Pduration(args []Result) Result {
if specifiedValue <= 0 {
return MakeErrorResultType(ErrorTypeNum, "PDURATION requires specified value to be positive")
}
return MakeNumberResult((math.Log10(specifiedValue) - math.Log10(currentValue)) / math.Log10(1 + rate))
return MakeNumberResult((math.Log10(specifiedValue) - math.Log10(currentValue)) / math.Log10(1+rate))
}
// Pmt implements the Excel PMT function.
@ -1766,11 +1766,11 @@ func Pmt(args []Result) Result {
if rate == 0 {
result = (presentValue + futureValue) / nPer
} else {
term := math.Pow(1 + rate, nPer)
term := math.Pow(1+rate, nPer)
if t == 1 {
result = (futureValue * rate / (term - 1) + presentValue * rate / (1 - 1 / term)) / (1 + rate)
result = (futureValue*rate/(term-1) + presentValue*rate/(1-1/term)) / (1 + rate)
} else {
result = futureValue * rate / (term - 1) + presentValue * rate / (1 - 1 / term)
result = futureValue*rate/(term-1) + presentValue*rate/(1-1/term)
}
}
return MakeNumberResult(-result)
@ -1889,12 +1889,12 @@ func getPrice(settlementDate, maturityDate, rate, yield, redemption, freqF float
return 0, errResult
}
a := coupdaybs(settlementDate, maturityDate, freq, basis)
ret := redemption / math.Pow(1 + yield / freqF, n - 1 + dsc)
ret := redemption / math.Pow(1+yield/freqF, n-1+dsc)
ret -= 100 * rate / freqF * a / e
t1 := 100 * rate / freqF
t2 := 1 + yield / freqF
t2 := 1 + yield/freqF
for k := 0.0; k < n; k++ {
ret += t1 / math.Pow(t2, k + dsc)
ret += t1 / math.Pow(t2, k+dsc)
}
return ret, MakeEmptyResult()
}
@ -1937,7 +1937,7 @@ func Pricedisc(args []Result) Result {
if errResult.Type == ResultTypeError {
return errResult
}
return MakeNumberResult(redemption * (1 - discount * yf))
return MakeNumberResult(redemption * (1 - discount*yf))
}
// Pricemat implements the Excel PRICEMAT function.
@ -1994,10 +1994,10 @@ func Pricemat(args []Result) Result {
return errResult
}
num := 1 + dim * rate
den := 1 + dsm * yield
num := 1 + dim*rate
den := 1 + dsm*yield
return MakeNumberResult((num / den - dis * rate) * 100)
return MakeNumberResult((num/den - dis*rate) * 100)
}
// Pv implements the Excel PV function.
@ -2039,9 +2039,9 @@ func Pv(args []Result) Result {
}
}
if rate == 0 {
return MakeNumberResult(-pmt * nPer - futureValue)
return MakeNumberResult(-pmt*nPer - futureValue)
} else {
return MakeNumberResult((((1 - math.Pow(1 + rate, nPer)) / rate) * pmt * (1 + rate * t) - futureValue) / math.Pow(1 + rate, nPer))
return MakeNumberResult((((1-math.Pow(1+rate, nPer))/rate)*pmt*(1+rate*t) - futureValue) / math.Pow(1+rate, nPer))
}
}
@ -2098,13 +2098,13 @@ func Rate(args []Result) Result {
rate := guess
for iter < maxIter && !close {
t1 := math.Pow(rate + 1, nPer)
t2 := math.Pow(rate + 1, nPer - 1)
rt := rate * t + 1
t1 := math.Pow(rate+1, nPer)
t2 := math.Pow(rate+1, nPer-1)
rt := rate*t + 1
p0 := pmt * (t1 - 1)
f1 := futureValue + t1 * presentValue + p0 * rt / rate
f2 := nPer * t2 * presentValue - p0 * rt / math.Pow(rate, 2)
f3 := (nPer * pmt * t2 * rt + p0 * t) / rate
f1 := futureValue + t1*presentValue + p0*rt/rate
f2 := nPer*t2*presentValue - p0*rt/math.Pow(rate, 2)
f3 := (nPer*pmt*t2*rt + p0*t) / rate
delta := f1 / (f2 + f3)
if math.Abs(delta) < epsMax {
@ -2155,7 +2155,7 @@ func Received(args []Result) Result {
if errResult.Type == ResultTypeError {
return errResult
}
return MakeNumberResult(investment / (1 - discount * frac))
return MakeNumberResult(investment / (1 - discount*frac))
}
// Rri implements the Excel RRI function.
@ -2185,7 +2185,7 @@ func Rri(args []Result) Result {
return MakeErrorResultType(ErrorTypeNum, "RRI requires future value to be non negative")
}
return MakeNumberResult(math.Pow(futureValue / presentValue, 1 / nPer) - 1)
return MakeNumberResult(math.Pow(futureValue/presentValue, 1/nPer) - 1)
}
// Sln implements the Excel SLN function.
@ -2209,7 +2209,7 @@ func Sln(args []Result) Result {
return MakeErrorResultType(ErrorTypeDivideByZero, "SLN requires life to be non zero")
}
return MakeNumberResult((cost - salvage ) / life)
return MakeNumberResult((cost - salvage) / life)
}
// Syd implements the Excel SYD function.
@ -2269,7 +2269,7 @@ func Tbilleq(args []Result) Result {
if discount <= 0 {
return MakeErrorResultType(ErrorTypeNum, "TBILLEQ requires discount to be positive number argument")
}
return MakeNumberResult((365 * discount) / (360 - discount * dsm))
return MakeNumberResult((365 * discount) / (360 - discount*dsm))
}
// Tbillprice implements the Excel TBILLPRICE function.
@ -2292,7 +2292,7 @@ func Tbillprice(args []Result) Result {
if discount <= 0 {
return MakeErrorResultType(ErrorTypeNum, "TBILLPRICE requires discount to be positive number argument")
}
return MakeNumberResult(100 * (1 - discount * dsm / 360))
return MakeNumberResult(100 * (1 - discount*dsm/360))
}
// Tbillyield implements the Excel TBILLYIELD function.
@ -2316,7 +2316,7 @@ func Tbillyield(args []Result) Result {
return MakeErrorResultType(ErrorTypeNum, "TBILLYIELD requires pr to be positive number argument")
}
m1 := (100 - pr) / pr
m2 := 360 /dsm
m2 := 360 / dsm
return MakeNumberResult(m1 * m2)
}
@ -2396,8 +2396,8 @@ func Vdb(args []Result) Result {
if noSwitch {
for i := startInt + 1; i <= endInt; i++ {
term := getDDB(cost, salvage, life, i, factor)
if i == startInt + 1 {
term *= math.Min(endPeriod, startInt + 1) - startPeriod
if i == startInt+1 {
term *= math.Min(endPeriod, startInt+1) - startPeriod
} else if i == endInt {
term *= endPeriod + 1 - endInt
}
@ -2420,7 +2420,7 @@ func Vdb(args []Result) Result {
if factor != 0 {
cost -= interVDB(cost, salvage, life, life1, startPeriod, factor)
}
vdb = interVDB(cost, salvage, life, life - startPeriod, endPeriod - startPeriod, factor)
vdb = interVDB(cost, salvage, life, life-startPeriod, endPeriod-startPeriod, factor)
}
return MakeNumberResult(vdb)
}
@ -2466,9 +2466,9 @@ func getDDB(cost, salvage, life, period, factor float64) float64 {
oldValue = 0
}
} else {
oldValue = cost * math.Pow(1 - rate, period - 1)
oldValue = cost * math.Pow(1-rate, period-1)
}
newValue := cost * math.Pow(1 - rate, period)
newValue := cost * math.Pow(1-rate, period)
var ddb float64
@ -2522,11 +2522,11 @@ func Yielddisc(args []Result) Result {
return errResult
}
return MakeNumberResult((redemption / pr - 1) / frac)
return MakeNumberResult((redemption/pr - 1) / frac)
}
func approxEqual(a, b float64) bool {
return math.Abs(a - b) < 1.0e-6
return math.Abs(a-b) < 1.0e-6
}
// Xirr implements the Excel XIRR function.
@ -2575,14 +2575,14 @@ func Xnpv(args []Result) Result {
xnpv := 0.0
firstDate := dates[0]
for i, value := range values {
xnpv += value / math.Pow(1 + rate, (dates[i] - firstDate) / 365)
xnpv += value / math.Pow(1+rate, (dates[i]-firstDate)/365)
}
return MakeNumberResult(xnpv)
}
type xargs struct {
values []float64
dates []float64
dates []float64
}
func getXargs(valuesR, datesR Result, funcName string) (*xargs, Result) {
@ -2615,7 +2615,7 @@ func getXargs(valuesR, datesR Result, funcName string) (*xargs, Result) {
if vR.Type == ResultTypeNumber && !vR.IsBoolean {
newDate := float64(int(vR.ValueNumber))
if newDate < lastDate {
return nil, MakeErrorResultType(ErrorTypeNum, funcName + " requires dates to be in ascending order")
return nil, MakeErrorResultType(ErrorTypeNum, funcName+" requires dates to be in ascending order")
}
dates = append(dates, newDate)
lastDate = newDate
@ -2724,7 +2724,7 @@ func Yield(args []Result) Result {
yield2 = yieldN
price2 = priceN
}
yieldN = yield2 - (yield2 - yield1) * ((pr - price2) / (price1 - price2))
yieldN = yield2 - (yield2-yield1)*((pr-price2)/(price1-price2))
}
}
@ -2785,8 +2785,8 @@ func Yieldmat(args []Result) Result {
return errResult
}
y := 1 + dim * rate
y /= pr / 100 + dis * rate
y := 1 + dim*rate
y /= pr/100 + dis*rate
y--
y /= dsm

View File

@ -11,9 +11,9 @@ import (
"fmt"
"strings"
"github.com/unidoc/unioffice/internal/mergesort"
"github.com/unidoc/unioffice/internal/wildcard"
"github.com/unidoc/unioffice/spreadsheet/reference"
"github.com/unidoc/unioffice/internal/mergesort"
)
func init() {
@ -63,7 +63,7 @@ func Column(args []Result) Result {
if err != nil {
return MakeErrorResult("Incorrect reference: " + ref.Value)
}
return MakeNumberResult(float64(cr.ColumnIdx+1))
return MakeNumberResult(float64(cr.ColumnIdx + 1))
}
// Columns implements the Excel COLUMNS function.
@ -226,13 +226,13 @@ func Match(args []Result) Result {
case 0:
for i, value := range values {
if compareForMatch(value, criteria) {
return MakeNumberResult(float64(i+1))
return MakeNumberResult(float64(i + 1))
}
}
case -1:
for i := 0; i < len(values); i++ {
if compareForMatch(values[i], criteria) {
return MakeNumberResult(float64(i+1))
return MakeNumberResult(float64(i + 1))
}
if criteria.isNumber && (values[i].ValueNumber < criteria.cNum) {
if i == 0 {
@ -244,7 +244,7 @@ func Match(args []Result) Result {
case 1:
for i := 0; i < len(values); i++ {
if compareForMatch(values[i], criteria) {
return MakeNumberResult(float64(i+1))
return MakeNumberResult(float64(i + 1))
}
if criteria.isNumber && (values[i].ValueNumber > criteria.cNum) {
if i == 0 {
@ -738,11 +738,11 @@ func kth(args []Result, large bool) Result {
}
kfloat := args[1].ValueNumber
if kfloat < 1 {
return MakeErrorResultType(ErrorTypeNum, funcName + " requires second argument of type number more than 0")
return MakeErrorResultType(ErrorTypeNum, funcName+" requires second argument of type number more than 0")
}
k := int(kfloat)
if float64(k) != kfloat {
return MakeErrorResultType(ErrorTypeNum, funcName + " requires second argument of type number more than 0")
return MakeErrorResultType(ErrorTypeNum, funcName+" requires second argument of type number more than 0")
}
unsorted := []float64{}
for _, row := range arr {
@ -753,12 +753,12 @@ func kth(args []Result, large bool) Result {
}
}
if k > len(unsorted) {
return MakeErrorResultType(ErrorTypeNum, funcName + " requires second argument of type number less or equal than the number of numbers in the array")
return MakeErrorResultType(ErrorTypeNum, funcName+" requires second argument of type number less or equal than the number of numbers in the array")
}
sorted := mergesort.MergeSort(unsorted)
if large {
return MakeNumberResult(sorted[len(sorted) - k])
return MakeNumberResult(sorted[len(sorted)-k])
} else {
return MakeNumberResult(sorted[k - 1])
return MakeNumberResult(sorted[k-1])
}
}

View File

@ -40,21 +40,21 @@ var bs string = string([]byte{92})
var integer, finance, intSep, intPar, decimal, decJust, decPar, par, percent, intCur, cur, curLabel, mdy, dmy, sci *regexp.Regexp
func initRegexpInformation() {
integer = regexp.MustCompile(`^0+$`) // 12345
intSep = regexp.MustCompile("^((#|0)+,)+(#|0)+(;|$)") // 123,456,789
intPar = regexp.MustCompile("^(#|0|,)*_\\);") // (123,456,789)
finance = regexp.MustCompile("^0+\\.(0+)$") // 1.23
decimal = regexp.MustCompile("^((#|0)+,)+(#|0)+\\.(0+).*(;|$)") // 1.234
decJust = regexp.MustCompile("^(_|-| )+\\* #+,#+0\\.(0+).*;") // 1.234 with justified horizontal alignment
decPar = regexp.MustCompile("^((#|0)+,)+(#|0)+\\.((#|0)+)_\\).*;") // (1.234)
percent = regexp.MustCompile("^(#|0)+\\.((#|0)+)%$") // 12.34%
intCur = regexp.MustCompile("\\[\\$\\$-.+\\](\\* )?(#|0)+,(#|0)+;") // $1,234
cur = regexp.MustCompile("\\[\\$\\$-.+\\](\\* )?(#|0)+,(#|0)+\\.((#|0|-)+).*;") // $1,234.56
curLabel = regexp.MustCompile("^((#|0)+,)+(#|0)+(\\.((#|0|-)+))?.+\\[\\$.+\\].*;") // 1,234.56 USD
mdy = regexp.MustCompile("^M+(/| |,|\"|" + bs + bs + ")+D+(/| |,|\"|" + bs + bs + ")+Y+$") // 01/21/2019
integer = regexp.MustCompile(`^0+$`) // 12345
intSep = regexp.MustCompile("^((#|0)+,)+(#|0)+(;|$)") // 123,456,789
intPar = regexp.MustCompile("^(#|0|,)*_\\);") // (123,456,789)
finance = regexp.MustCompile("^0+\\.(0+)$") // 1.23
decimal = regexp.MustCompile("^((#|0)+,)+(#|0)+\\.(0+).*(;|$)") // 1.234
decJust = regexp.MustCompile("^(_|-| )+\\* #+,#+0\\.(0+).*;") // 1.234 with justified horizontal alignment
decPar = regexp.MustCompile("^((#|0)+,)+(#|0)+\\.((#|0)+)_\\).*;") // (1.234)
percent = regexp.MustCompile("^(#|0)+\\.((#|0)+)%$") // 12.34%
intCur = regexp.MustCompile("\\[\\$\\$-.+\\](\\* )?(#|0)+,(#|0)+;") // $1,234
cur = regexp.MustCompile("\\[\\$\\$-.+\\](\\* )?(#|0)+,(#|0)+\\.((#|0|-)+).*;") // $1,234.56
curLabel = regexp.MustCompile("^((#|0)+,)+(#|0)+(\\.((#|0|-)+))?.+\\[\\$.+\\].*;") // 1,234.56 USD
mdy = regexp.MustCompile("^M+(/| |,|\"|" + bs + bs + ")+D+(/| |,|\"|" + bs + bs + ")+Y+$") // 01/21/2019
dmy = regexp.MustCompile("^D+(/| |\\.|\"|" + bs + bs + ")+M+(/| |\\.|\"|" + bs + bs + ")+Y+$") // 21. Jan. 2019
sci = regexp.MustCompile("^(#|0)+\\.((#|0)*)E\\+(#|0)+(;|$)") // 1.02E+002
par = regexp.MustCompile("^.*_\\).*;") // (anything in parentheses)
sci = regexp.MustCompile("^(#|0)+\\.((#|0)*)E\\+(#|0)+(;|$)") // 1.02E+002
par = regexp.MustCompile("^.*_\\).*;") // (anything in parentheses)
}
// NA is an implementation of the Excel NA() function that just returns the #N/A! error.
@ -87,7 +87,7 @@ func Cell(ctx Context, ev Evaluator, args []Result) Result {
if err != nil {
return MakeErrorResult("Incorrect reference: " + refStr)
}
address := "$"+cr.Column+"$"+strconv.Itoa(int(cr.RowIdx))
address := "$" + cr.Column + "$" + strconv.Itoa(int(cr.RowIdx))
if cr.SheetName != "" {
address = cr.SheetName + "!" + address
}
@ -97,7 +97,7 @@ func Cell(ctx Context, ev Evaluator, args []Result) Result {
if err != nil {
return MakeErrorResult("Incorrect reference: " + refStr)
}
return MakeNumberResult(float64(cr.ColumnIdx+1))
return MakeNumberResult(float64(cr.ColumnIdx + 1))
case "color":
red := strings.Contains(ctx.GetFormat(refStr), "[RED]")
return MakeBoolResult(red)
@ -198,7 +198,7 @@ func Cell(ctx Context, ev Evaluator, args []Result) Result {
return MakeNumberResult(ctx.Sheet(cr.SheetName).GetWidth(int(cr.ColumnIdx)))
}
}
return MakeErrorResult("Incorrect first argument of CELL: "+typ.ValueString)
return MakeErrorResult("Incorrect first argument of CELL: " + typ.ValueString)
}
func itemFromEndLength(submatch []string, additionalShift int) string {
@ -240,7 +240,7 @@ func IsEven(args []Result) Result {
if args[0].Type != ResultTypeNumber {
return MakeErrorResult("ISEVEN accepts a numeric argument")
}
value:= int(args[0].ValueNumber)
value := int(args[0].ValueNumber)
return MakeBoolResult(value == value/2*2)
}
@ -339,7 +339,7 @@ func IsOdd(args []Result) Result {
if args[0].Type != ResultTypeNumber {
return MakeErrorResult("ISODD accepts a numeric argument")
}
value:= int(args[0].ValueNumber)
value := int(args[0].ValueNumber)
return MakeBoolResult(value != value/2*2)
}

View File

@ -358,7 +358,7 @@ func Ifs(args []Result) Result {
if len(args) < 2 {
return MakeErrorResult("IFS requires at least two arguments")
}
for i := 0; i < len(args)-1; i+=2 {
for i := 0; i < len(args)-1; i += 2 {
if args[i].ValueNumber == 1 {
return args[i+1]
}

View File

@ -42,9 +42,9 @@ var number, eq, g, l, ge, le *regexp.Regexp
func initRegexpStatistical() {
number = regexp.MustCompile(`^([0-9]+)$`)
eq = regexp.MustCompile(`^=(.*)$`) // =-12345.67, =A6
l = regexp.MustCompile(`^<(.*)$`) // <-12345.67, <A6
g = regexp.MustCompile(`^>(.*)$`) // >-12345.67, >A6
eq = regexp.MustCompile(`^=(.*)$`) // =-12345.67, =A6
l = regexp.MustCompile(`^<(.*)$`) // <-12345.67, <A6
g = regexp.MustCompile(`^>(.*)$`) // >-12345.67, >A6
le = regexp.MustCompile(`^<=(.*)$`) // <=-12345.67, <=A6
ge = regexp.MustCompile(`^>=(.*)$`) // >=-12345.67, >=A6
}
@ -149,9 +149,9 @@ func CountBlank(args []Result) Result {
type criteriaParsed struct {
isNumber bool
cNum float64
cStr string
cRegex *criteriaRegex
cNum float64
cStr string
cRegex *criteriaRegex
}
const (
@ -164,8 +164,8 @@ const (
)
type criteriaRegex struct {
regexType byte // type of condition
compareWith string // value to apply condition to
regexType byte // type of condition
compareWith string // value to apply condition to
}
func parseCriteria(criteria Result) *criteriaParsed {
@ -268,7 +268,7 @@ func checkIfsRanges(args []Result, sumRange bool, fnName string) Result {
rangeWidth := -1
rangeHeight := -1
for i := 0; i < argsNum; i+=2 {
for i := 0; i < argsNum; i += 2 {
arrResult := args[i]
if arrResult.Type != ResultTypeArray && arrResult.Type != ResultTypeList {
return MakeErrorResult(fnName + " requires ranges of type list or array")
@ -291,7 +291,7 @@ func checkIfsRanges(args []Result, sumRange bool, fnName string) Result {
func getIfsMatch(args []Result) []rangeIndex {
toLook := []rangeIndex{}
argsNum := len(args)
for i := 0; i < argsNum-1; i+=2 {
for i := 0; i < argsNum-1; i += 2 {
found := []rangeIndex{}
arr := arrayFromRange(args[i])
criteria := parseCriteria(args[i+1])
@ -403,7 +403,7 @@ func max(args []Result, isMaxA bool) Result {
v = crit
}
default:
unioffice.Log("unhandled " + fName + "() argument type %s", a.Type)
unioffice.Log("unhandled "+fName+"() argument type %s", a.Type)
}
}
if v == -math.MaxFloat64 {
@ -454,7 +454,7 @@ func min(args []Result, isMinA bool) Result {
v = crit
}
default:
unioffice.Log("unhandled " + fName + "() argument type %s", a.Type)
unioffice.Log("unhandled "+fName+"() argument type %s", a.Type)
}
}
if v == math.MaxFloat64 {

View File

@ -164,7 +164,7 @@ func Exact(args []Result) Result {
type parsedSearchObject struct {
findText string
text string
text string
position int
}
@ -257,7 +257,7 @@ func Findb(ctx Context, ev Evaluator, args []Result) Result {
for i := range text {
if i != 0 {
add := 1
if i - lastIndex > 1 {
if i-lastIndex > 1 {
add = 2
}
stepsCounter += add
@ -422,7 +422,7 @@ func Mid(args []Result) Result {
}
startNum--
endNum := startNum + numChars
if endNum > l + 1 {
if endNum > l+1 {
return MakeStringResult(text[startNum:])
} else {
return MakeStringResult(text[startNum:endNum])
@ -567,7 +567,7 @@ func Searchb(ctx Context, ev Evaluator, args []Result) Result {
for i := range text {
if i != 0 {
add := 1
if i - lastIndex > 1 {
if i-lastIndex > 1 {
add = 2
}
stepsCounter += add
@ -728,9 +728,9 @@ func Value(args []Result) Result {
}
type parsedReplaceObject struct {
text string
startPos int
length int
text string
startPos int
length int
textToReplace string
}
@ -879,11 +879,11 @@ func getNumber(arg Result, funcName, argName string) (float64, Result) {
case ResultTypeNumber:
return arg.ValueNumber, empty
case ResultTypeString:
f, err := strconv.ParseFloat(arg.ValueString, 64)
if err != nil {
return 0, MakeErrorResult(argName + " should be a number for " + funcName)
}
return f, empty
f, err := strconv.ParseFloat(arg.ValueString, 64)
if err != nil {
return 0, MakeErrorResult(argName + " should be a number for " + funcName)
}
return f, empty
default:
return 0, MakeErrorResult(funcName + " requires " + argName + " to be a number or empty")
}

View File

@ -9,6 +9,7 @@ package formula
import (
"bytes"
"github.com/unidoc/unioffice/spreadsheet/update"
)

View File

@ -22,7 +22,7 @@ import (
// Input is an input formula string.
// Expected is the expected output of the formula as a string of format: "value type". It depends on Input and workbook that is being worked with.
type testStruct struct {
Input string
Input string
Expected string
}
@ -96,7 +96,7 @@ func TestCell(t *testing.T) {
// cells with number, needed for testing different formats
for i := 1; i <= 25; i++ {
sheet.Cell("A"+strconv.Itoa(i)).SetNumber(-12345.6789)
sheet.Cell("A" + strconv.Itoa(i)).SetNumber(-12345.6789)
}
// cells with string values, needed for testing different alignments
@ -366,7 +366,6 @@ func TestCountIfs(t *testing.T) {
sheet.Cell("B9").SetNumber(2)
sheet.Cell("B10").SetNumber(1)
ctx := sheet.FormulaContext()
runTests(t, ctx, td)
@ -1354,14 +1353,14 @@ func TestDuration(t *testing.T) {
ss := spreadsheet.New()
sheet := ss.AddSheet()
sheet.Cell("A1").SetTime(time.Date(2018, time.July, 1, 0, 0, 0, 0, time.UTC)) // settlement date
sheet.Cell("A1").SetTime(time.Date(2018, time.July, 1, 0, 0, 0, 0, time.UTC)) // settlement date
sheet.Cell("A2").SetTime(time.Date(2048, time.January, 1, 0, 0, 0, 0, time.UTC)) // maturity date
sheet.Cell("A3").SetNumber(0.08) // coupon rate
sheet.Cell("A4").SetNumber(0.09) // yield rate
sheet.Cell("A5").SetNumber(2) // frequency of payments
sheet.Cell("A6").SetNumber(0) // basis
sheet.Cell("A7").SetString("07/01/2018") // settlement date in string format
sheet.Cell("A8").SetString("01/01/2048") // maturity date in string format
sheet.Cell("A3").SetNumber(0.08) // coupon rate
sheet.Cell("A4").SetNumber(0.09) // yield rate
sheet.Cell("A5").SetNumber(2) // frequency of payments
sheet.Cell("A6").SetNumber(0) // basis
sheet.Cell("A7").SetString("07/01/2018") // settlement date in string format
sheet.Cell("A8").SetString("01/01/2048") // maturity date in string format
td := []testStruct{
{`=DURATION(A1,A2,A3,A4,A5)`, `10.9191452815 ResultTypeNumber`},
@ -1382,12 +1381,12 @@ func TestMduration(t *testing.T) {
sheet.Cell("A1").SetTime(time.Date(2008, time.January, 1, 0, 0, 0, 0, time.UTC)) // settlement date
sheet.Cell("A2").SetTime(time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC)) // maturity date
sheet.Cell("A3").SetNumber(0.08) // coupon rate
sheet.Cell("A4").SetNumber(0.09) // yield rate
sheet.Cell("A5").SetNumber(2) // frequency of payments
sheet.Cell("A6").SetNumber(0) // basis
sheet.Cell("A7").SetString("01/01/2008") // settlement date in string format
sheet.Cell("A8").SetString("01/01/2016") // maturity date in string format
sheet.Cell("A3").SetNumber(0.08) // coupon rate
sheet.Cell("A4").SetNumber(0.09) // yield rate
sheet.Cell("A5").SetNumber(2) // frequency of payments
sheet.Cell("A6").SetNumber(0) // basis
sheet.Cell("A7").SetString("01/01/2008") // settlement date in string format
sheet.Cell("A8").SetString("01/01/2016") // maturity date in string format
td := []testStruct{
{`=MDURATION(A1,A2,A3,A4,A5)`, `5.73566981391 ResultTypeNumber`},

View File

@ -8,7 +8,7 @@
package formula
// noCache is a struct with collection of caching methods stubs intended for evaluators without cache.
type noCache struct {}
type noCache struct{}
func (nc *noCache) SetCache(key string, value Result) {}

View File

@ -17,7 +17,7 @@ import (
// PrefixHorizontalRange is a range expression that when evaluated returns a list of Results from references like Sheet1!1:4 (all cells from rows 1 to 4 of sheet 'Sheet1').
type PrefixHorizontalRange struct {
pfx Expression
pfx Expression
rowFrom, rowTo int
}

View File

@ -16,7 +16,7 @@ import (
// PrefixVerticalRange is a range expression that when evaluated returns a list of Results from references like Sheet1!AA:IJ (all cells from columns AA to IJ of sheet 'Sheet1').
type PrefixVerticalRange struct {
pfx Expression
pfx Expression
colFrom, colTo string
}

View File

@ -14,9 +14,9 @@ import (
)
type MergedCell struct {
wb *Workbook
ws *sml.Worksheet
x *sml.CT_MergeCell
wb *Workbook
sheet *Sheet
x *sml.CT_MergeCell
}
// X returns the inner wrapped XML type.
@ -39,7 +39,7 @@ func (s MergedCell) Cell() Cell {
ref := s.Reference()
if idx := strings.Index(s.Reference(), ":"); idx != -1 {
ref = ref[0:idx]
return Sheet{w: s.wb, x: s.ws}.Cell(ref)
return s.sheet.Cell(ref)
}
// couldn't find it, log an error?
return Cell{}

View File

@ -12,8 +12,8 @@ import (
"fmt"
"io"
"io/ioutil"
"path/filepath"
"os"
"path/filepath"
"github.com/unidoc/unioffice"
"github.com/unidoc/unioffice/zippkg"

View File

@ -18,9 +18,9 @@ import (
// Row is a row within a spreadsheet.
type Row struct {
w *Workbook
s *sml.Worksheet
x *sml.CT_Row
w *Workbook
sheet *Sheet
x *sml.CT_Row
}
// X returns the inner wrapped XML type.
@ -91,7 +91,7 @@ func (r Row) AddCell() Cell {
nextCellID = unioffice.Stringf("%s%d", reference.IndexToColumn(nextIdx), r.RowNumber())
}
c.RAttr = nextCellID
return Cell{r.w, r.s, r.x, c}
return Cell{r.w, r.sheet, r.x, c}
}
// Cells returns a slice of cells. The cells can be manipulated, but appending
@ -110,13 +110,24 @@ func (r Row) Cells() []Cell {
continue
}
currentIndex := int(ref.ColumnIdx)
if currentIndex - lastIndex > 1 {
for col := lastIndex + 1; col < currentIndex; col++ {
if currentIndex-lastIndex > 1 {
for col := lastIndex + 1; col < currentIndex; col++ {
ret = append(ret, r.Cell(reference.IndexToColumn(uint32(col))))
}
}
lastIndex = currentIndex
ret = append(ret, Cell{r.w, r.s, r.x, c})
ret = append(ret, Cell{r.w, r.sheet, r.x, c})
}
return ret
}
// CellsWithEmpty returns a slice of cells including empty ones from the first column to the last one used in the sheet.
// The cells can be manipulated, but appending to the slice will have no effect.
func (r Row) CellsWithEmpty(lastColIdx uint32) []Cell {
ret := []Cell{}
for columnIdx := uint32(0); columnIdx <= lastColIdx; columnIdx++ {
c := r.Cell(reference.IndexToColumn(columnIdx))
ret = append(ret, c)
}
return ret
}
@ -147,7 +158,7 @@ func (r Row) AddNamedCell(col string) Cell {
r.x.C = append(r.x.C[:indexToInsert], append([]*sml.CT_Cell{c}, r.x.C[indexToInsert:]...)...)
}
return Cell{r.w, r.s, r.x, c}
return Cell{r.w, r.sheet, r.x, c}
}
// Cell retrieves or adds a new cell to a row. Col is the column (e.g. 'A', 'B')
@ -155,7 +166,7 @@ func (r Row) Cell(col string) Cell {
name := fmt.Sprintf("%s%d", col, r.RowNumber())
for _, c := range r.x.C {
if c.RAttr != nil && *c.RAttr == name {
return Cell{r.w, r.s, r.x, c}
return Cell{r.w, r.sheet, r.x, c}
}
}
return r.AddNamedCell(col)

View File

@ -31,6 +31,22 @@ type Sheet struct {
x *sml.Worksheet
}
// MaxColumnIdx returns the max used column of the sheet.
func (s Sheet) MaxColumnIdx() uint32 {
maxColumnIdx := uint32(0)
for _, r := range s.Rows() {
cells := r.x.C
if len(cells) > 0 {
lastCell := cells[len(cells)-1]
ref, _ := reference.ParseCellReference(*lastCell.RAttr)
if maxColumnIdx < ref.ColumnIdx {
maxColumnIdx = ref.ColumnIdx
}
}
}
return maxColumnIdx
}
func (s Sheet) IsValid() bool {
return s.x != nil
}
@ -42,11 +58,11 @@ func (s Sheet) X() *sml.Worksheet {
// Row will return a row with a given row number, creating a new row if
// necessary.
func (s Sheet) Row(rowNum uint32) Row {
func (s *Sheet) Row(rowNum uint32) Row {
// see if the row exists
for _, r := range s.x.SheetData.Row {
if r.RAttr != nil && *r.RAttr == rowNum {
return Row{s.w, s.x, r}
return Row{s.w, s, r}
}
}
// create a new row
@ -54,7 +70,7 @@ func (s Sheet) Row(rowNum uint32) Row {
}
// Cell creates or returns a cell given a cell reference of the form 'A10'
func (s Sheet) Cell(cellRef string) Cell {
func (s *Sheet) Cell(cellRef string) Cell {
cref, err := reference.ParseCellReference(cellRef)
if err != nil {
unioffice.Log("error parsing cell reference: %s", err)
@ -66,7 +82,7 @@ func (s Sheet) Cell(cellRef string) Cell {
// AddNumberedRow adds a row with a given row number. If you reuse a row number
// the resulting file will fail validation and fail to open in Office programs. Use
// Row instead which creates a new row or returns an existing row.
func (s Sheet) AddNumberedRow(rowNum uint32) Row {
func (s *Sheet) AddNumberedRow(rowNum uint32) Row {
r := sml.NewCT_Row()
r.RAttr = unioffice.Uint32(rowNum)
s.x.SheetData.Row = append(s.x.SheetData.Row, r)
@ -84,22 +100,22 @@ func (s Sheet) AddNumberedRow(rowNum uint32) Row {
return *l < *r
})
return Row{s.w, s.x, r}
return Row{s.w, s, r}
}
// addNumberedRowFast is a fast path that can be used when adding consecutive
// rows and not skipping any.
func (s Sheet) addNumberedRowFast(rowNum uint32) Row {
func (s *Sheet) addNumberedRowFast(rowNum uint32) Row {
r := sml.NewCT_Row()
r.RAttr = unioffice.Uint32(rowNum)
s.x.SheetData.Row = append(s.x.SheetData.Row, r)
return Row{s.w, s.x, r}
return Row{s.w, s, r}
}
// AddRow adds a new row to a sheet. You can mix this with numbered rows,
// however it will get confusing. You should prefer to use either automatically
// numbered rows with AddRow or manually numbered rows with Row/AddNumberedRow
func (s Sheet) AddRow() Row {
func (s *Sheet) AddRow() Row {
maxRowID := uint32(0)
numRows := uint32(len(s.x.SheetData.Row))
@ -120,7 +136,7 @@ func (s Sheet) AddRow() Row {
// InsertRow inserts a new row into a spreadsheet at a particular row number. This
// row will now be the row number specified, and any rows after it will be renumbed.
func (s Sheet) InsertRow(rowNum int) Row {
func (s *Sheet) InsertRow(rowNum int) Row {
rIdx := uint32(rowNum)
// Renumber every row after the row we're inserting
@ -166,7 +182,7 @@ func (s Sheet) Name() string {
}
// SetName sets the sheet name.
func (s Sheet) SetName(name string) {
func (s *Sheet) SetName(name string) {
s.cts.NameAttr = name
}
@ -182,7 +198,7 @@ func (s Sheet) Validate() error {
return err
}
}
if err := s.cts.Validate(); err != nil {
if err := s.x.Validate(); err != nil {
return err
}
return s.x.Validate()
@ -249,21 +265,21 @@ func (s Sheet) validateMergedCells() error {
// ValidateWithPath validates the sheet passing path informaton for a better
// error message
func (s Sheet) ValidateWithPath(path string) error {
return s.cts.ValidateWithPath(path)
return s.x.ValidateWithPath(path)
}
// Rows returns all of the rows in a sheet.
func (s Sheet) Rows() []Row {
func (s *Sheet) Rows() []Row {
ret := []Row{}
for _, r := range s.x.SheetData.Row {
ret = append(ret, Row{s.w, s.x, r})
ret = append(ret, Row{s.w, s, r})
}
return ret
}
// SetDrawing sets the worksheet drawing. A worksheet can have a reference to a
// single drawing, but the drawing can have many charts.
func (s Sheet) SetDrawing(d Drawing) {
func (s *Sheet) SetDrawing(d Drawing) {
var rel common.Relationships
for i, wks := range s.w.xws {
if wks == s.x {
@ -288,7 +304,7 @@ func (s Sheet) SetDrawing(d Drawing) {
// AddHyperlink adds a hyperlink to a sheet. Adding the hyperlink to the sheet
// and setting it on a cell is more efficient than setting hyperlinks directly
// on a cell.
func (s Sheet) AddHyperlink(url string) common.Hyperlink {
func (s *Sheet) AddHyperlink(url string) common.Hyperlink {
// store the relationships so we don't need to do a lookup here?
for i, ws := range s.w.xws {
if ws == s.x {
@ -318,7 +334,7 @@ func (s Sheet) RangeReference(n string) string {
const autoFilterName = "_xlnm._FilterDatabase"
// ClearAutoFilter removes the autofilters from the sheet.
func (s Sheet) ClearAutoFilter() {
func (s *Sheet) ClearAutoFilter() {
s.x.AutoFilter = nil
sn := "'" + s.Name() + "'!"
// see if we have a defined auto filter name for the sheet
@ -336,7 +352,7 @@ func (s Sheet) ClearAutoFilter() {
// filters that are common for a header row. The RangeRef should be of the form
// "A1:C5" and cover the entire range of cells to be filtered, not just the
// header. SetAutoFilter replaces any existing auto filter on the sheet.
func (s Sheet) SetAutoFilter(rangeRef string) {
func (s *Sheet) SetAutoFilter(rangeRef string) {
// this should have no $ in it
rangeRef = strings.Replace(rangeRef, "$", "", -1)
@ -369,7 +385,7 @@ func (s Sheet) SetAutoFilter(rangeRef string) {
}
// AddMergedCells merges cells within a sheet.
func (s Sheet) AddMergedCells(fromRef, toRef string) MergedCell {
func (s *Sheet) AddMergedCells(fromRef, toRef string) MergedCell {
// TODO: we might need to actually create the merged cells if they don't
// exist, but it appears to work fine on both Excel and LibreOffice just
// creating the merged region
@ -383,24 +399,24 @@ func (s Sheet) AddMergedCells(fromRef, toRef string) MergedCell {
s.x.MergeCells.MergeCell = append(s.x.MergeCells.MergeCell, merge)
s.x.MergeCells.CountAttr = unioffice.Uint32(uint32(len(s.x.MergeCells.MergeCell)))
return MergedCell{s.w, s.x, merge}
return MergedCell{s.w, s, merge}
}
// MergedCells returns the merged cell regions within the sheet.
func (s Sheet) MergedCells() []MergedCell {
func (s *Sheet) MergedCells() []MergedCell {
if s.x.MergeCells == nil {
return nil
}
ret := []MergedCell{}
for _, c := range s.x.MergeCells.MergeCell {
ret = append(ret, MergedCell{s.w, s.x, c})
ret = append(ret, MergedCell{s.w, s, c})
}
return ret
}
// RemoveMergedCell removes merging from a cell range within a sheet. The cells
// that made up the merged cell remain, but are no lon merged.
func (s Sheet) RemoveMergedCell(mc MergedCell) {
func (s *Sheet) RemoveMergedCell(mc MergedCell) {
for i, c := range s.x.MergeCells.MergeCell {
if c == mc.X() {
copy(s.x.MergeCells.MergeCell[i:], s.x.MergeCells.MergeCell[i+1:])
@ -443,7 +459,7 @@ func (s Sheet) Extents() string {
}
// AddConditionalFormatting adds conditional formatting to the sheet.
func (s Sheet) AddConditionalFormatting(cellRanges []string) ConditionalFormatting {
func (s *Sheet) AddConditionalFormatting(cellRanges []string) ConditionalFormatting {
cfmt := sml.NewCT_ConditionalFormatting()
s.x.ConditionalFormatting = append(s.x.ConditionalFormatting, cfmt)
@ -460,7 +476,7 @@ func (s Sheet) AddConditionalFormatting(cellRanges []string) ConditionalFormatti
// can span multiple column indices, this method will return the column that
// applies to a column index if it exists or create a new column that only
// applies to the index passed in otherwise.
func (s Sheet) Column(idx uint32) Column {
func (s *Sheet) Column(idx uint32) Column {
// scan for any existing column that covers this index
for _, colSet := range s.x.Cols {
for _, col := range colSet.Col {
@ -487,7 +503,7 @@ func (s Sheet) Column(idx uint32) Column {
}
// Comments returns the comments for a sheet.
func (s Sheet) Comments() Comments {
func (s *Sheet) Comments() Comments {
for i, wks := range s.w.xws {
if wks == s.x {
if s.w.comments[i] == nil {
@ -518,7 +534,7 @@ func (s Sheet) Comments() Comments {
// the cells on the top,left,right,bottom and four corners. This function
// breaks apart a single border into its components and applies it to cells as
// needed to give the effect of a border applying to multiple cells.
func (s Sheet) SetBorder(cellRange string, border Border) error {
func (s *Sheet) SetBorder(cellRange string, border Border) error {
from, to, err := reference.ParseRangeReference(cellRange)
if err != nil {
return err
@ -605,7 +621,7 @@ func (s Sheet) SetBorder(cellRange string, border Border) error {
}
// AddDataValidation adds a data validation rule to a sheet.
func (s Sheet) AddDataValidation() DataValidation {
func (s *Sheet) AddDataValidation() DataValidation {
if s.x.DataValidations == nil {
s.x.DataValidations = sml.NewCT_DataValidations()
}
@ -724,7 +740,7 @@ func (s *Sheet) setArray(origin string, arr formula.Result) error {
sr := s.Row(cref.RowIdx + uint32(ir))
for ic, val := range row {
cell := sr.Cell(reference.IndexToColumn(cref.ColumnIdx + uint32(ic)))
if val.Type != formula.ResultTypeEmpty {
if val.Type != formula.ResultTypeEmpty {
if val.IsBoolean {
cell.SetBool(val.ValueNumber != 0)
} else {
@ -747,7 +763,7 @@ func (s *Sheet) setList(origin string, list formula.Result) error {
sr := s.Row(cref.RowIdx)
for ic, val := range list.ValueList {
cell := sr.Cell(reference.IndexToColumn(cref.ColumnIdx + uint32(ic)))
if val.Type != formula.ResultTypeEmpty {
if val.Type != formula.ResultTypeEmpty {
if val.IsBoolean {
cell.SetBool(val.ValueNumber != 0)
} else {
@ -860,8 +876,8 @@ func (s *Sheet) Sort(column string, firstRow uint32, order SortOrder) {
cmp := Comparer{Order: order}
sort.Slice(sheetData, func(i, j int) bool {
return cmp.LessRows(column,
Row{s.w, s.x, sheetData[i]},
Row{s.w, s.x, sheetData[j]})
Row{s.w, s, sheetData[i]},
Row{s.w, s, sheetData[j]})
})
// since we probably moved some rows, we need to go and fix up their row
@ -928,8 +944,8 @@ func (s *Sheet) RemoveColumn(column string) error {
func (s *Sheet) updateAfterRemove(columnIdx uint32, updateType update.UpdateAction) error {
ownSheetName := s.Name()
q := &update.UpdateQuery{
UpdateType: updateType,
ColumnIdx: columnIdx,
UpdateType: updateType,
ColumnIdx: columnIdx,
SheetToUpdate: ownSheetName,
}
for _, sheet := range s.w.Sheets() {
@ -1017,11 +1033,11 @@ func (s *Sheet) removeColumnFromNamedRanges(columnIdx uint32) error {
}
}
}
sheetTables := s.w.tables[startFromTable:startFromTable + numTables]
sheetTables := s.w.tables[startFromTable : startFromTable+numTables]
for tblIndex, tbl := range sheetTables {
newTable := tbl
newTable.RefAttr = moveRangeLeft(newTable.RefAttr, columnIdx, false)
s.w.tables[startFromTable + tblIndex] = newTable
s.w.tables[startFromTable+tblIndex] = newTable
}
}
return nil

View File

@ -357,7 +357,7 @@ func TestRemoveColumn(t *testing.T) {
sheet.RemoveColumn("C")
expected := []float64{5,4,2,1,0}
expected := []float64{5, 4, 2, 1, 0}
for i := 0; i <= 4; i++ {
column := reference.IndexToColumn(uint32(i))
@ -369,3 +369,16 @@ func TestRemoveColumn(t *testing.T) {
}
}
}
func TestCellsWithEmpty(t *testing.T) {
wb := spreadsheet.New()
sheet := wb.AddSheet()
sheet.Cell("A1").SetNumber(1)
sheet.Cell("F2").SetNumber(1)
rows := sheet.Rows()
exp := 6
got := len(rows[0].CellsWithEmpty(sheet.MaxColumnIdx()))
if got != exp {
t.Errorf("expected %d cells in row, got %d", exp, got)
}
}

View File

@ -10,6 +10,7 @@ package update
// UpdateAction is the type for update types constants.
type UpdateAction byte
const (
// UpdateActionRemoveColumn means updating references after removing a column.
UpdateActionRemoveColumn UpdateAction = iota

View File

@ -237,7 +237,8 @@ func (wb *Workbook) CopySheet(ind int, copiedSheetName string) (Sheet, error) {
wb.comments = append(wb.comments, &copiedComments)
}
return Sheet{wb, &copiedSheet, &copiedWs}, nil
sheet := Sheet{wb, &copiedSheet, &copiedWs}
return sheet, nil
}
// CopySheetByName copies the existing sheet with the name `name` and puts its copy with the name `copiedSheetName`.
@ -438,7 +439,8 @@ func (wb *Workbook) Sheets() []Sheet {
ret := []Sheet{}
for i, wks := range wb.xws {
r := wb.x.Sheets.Sheet[i]
ret = append(ret, Sheet{wb, r, wks})
sheet := Sheet{wb, r, wks}
ret = append(ret, sheet)
}
return ret
}