mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +08:00
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:
parent
5f9c94bf84
commit
72ad869a28
36
_examples/spreadsheet/cells-with-empty/main.go
Normal file
36
_examples/spreadsheet/cells-with-empty/main.go
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
BIN
_examples/spreadsheet/cells-with-empty/test.xlsx
Normal file
BIN
_examples/spreadsheet/cells-with-empty/test.xlsx
Normal file
Binary file not shown.
@ -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")
|
||||
}
|
||||
|
@ -395,7 +395,6 @@ func (p *Presentation) Save(w io.Writer) error {
|
||||
r.Properties().SetSolidFill(color.Red)
|
||||
}
|
||||
|
||||
|
||||
dt := unioffice.DocTypePresentation
|
||||
|
||||
z := zip.NewWriter(w)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ package formula
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/unidoc/unioffice/spreadsheet/update"
|
||||
)
|
||||
|
||||
|
@ -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`},
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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{}
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/unidoc/unioffice"
|
||||
"github.com/unidoc/unioffice/zippkg"
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user