Edge implements Ctrl+arrow jumps (to the end of a filled block, or to the next filled cell), UsedRange backs Ctrl+End and Ctrl+A, and RangeStats computes Sum/Count over a range, visiting whichever is smaller: the range or the filled cells.
- commit
- 5acda22f33b03e0b53bd2f01173ddc7380c2b4c6
- parent
- 4626856f1a364dee3432ee909ef030a7fa316ce9
- author
- Dami <dami.etoile@gmail.com>
| A | internal/ | 84 | ++++++++++++++++++++++++++++++ |
| A | internal/ | 50 | ++++++++++++++++++ |
@@ -0,0 +1,84 @@
1+package sheet2+3+// UsedRange returns the smallest range from A1 covering every non-blank4+// cell, and false if the sheet is empty.5+func (s *Sheet) UsedRange() (Rect, bool) {6+ if len(s.cells) == 0 {7+ return Rect{}, false8+ }9+ var last Addr10+ for a := range s.cells {11+ last.Col = max(last.Col, a.Col)12+ last.Row = max(last.Row, a.Row)13+ }14+ return Rect{To: last}, true15+}16+17+// Edge returns where a data-edge jump (Ctrl+arrow in Excel, End+arrow in18+// 1-2-3) from a in direction (dc, dr) lands. Inside a block of filled cells19+// it stops at the block's last cell; from a blank cell, or at the end of a20+// block, it goes to the next filled cell. With nothing filled ahead it21+// stops at the edge of the worksheet.22+func (s *Sheet) Edge(a Addr, dc, dr int) Addr {23+ filled := func(a Addr) bool { return s.cells[a] != nil }24+ next := Addr{Col: a.Col + dc, Row: a.Row + dr}25+ if !next.Valid() {26+ return a27+ }28+ if filled(a) && filled(next) {29+ for {30+ n := Addr{Col: next.Col + dc, Row: next.Row + dr}31+ if !n.Valid() || !filled(n) {32+ return next33+ }34+ next = n35+ }36+ }37+ for {38+ if filled(next) {39+ return next40+ }41+ n := Addr{Col: next.Col + dc, Row: next.Row + dr}42+ if !n.Valid() {43+ return next44+ }45+ next = n46+ }47+}48+49+// Stats summarizes the values in r, as shown in the status line for a50+// selection. Count is non-blank cells, Nums is numeric cells.51+type Stats struct {52+ Sum float6453+ Count, Nums int54+}55+56+// RangeStats computes Stats over r, visiting whichever is smaller: the57+// cells in r or the non-blank cells in the sheet.58+func (s *Sheet) RangeStats(r Rect) Stats {59+ var st Stats60+ add := func(c *Cell) {61+ st.Count++62+ if c.Value.Kind == Number {63+ st.Nums++64+ st.Sum += c.Value.Num65+ }66+ }67+ area := (r.To.Col - r.From.Col + 1) * (r.To.Row - r.From.Row + 1)68+ if area <= len(s.cells) {69+ for row := r.From.Row; row <= r.To.Row; row++ {70+ for col := r.From.Col; col <= r.To.Col; col++ {71+ if c := s.cells[Addr{Col: col, Row: row}]; c != nil {72+ add(c)73+ }74+ }75+ }76+ return st77+ }78+ for a, c := range s.cells {79+ if r.Contains(a) {80+ add(c)81+ }82+ }83+ return st84+}@@ -0,0 +1,50 @@
1+package sheet2+3+import "testing"4+5+func TestEdge(t *testing.T) {6+ s := New()7+ for _, a := range []string{"A1", "A2", "A3", "A6", "A7"} {8+ s.Set(at(a), "1")9+ }10+ tests := []struct {11+ from string12+ dc, dr int13+ want string14+ }{15+ {"A1", 0, 1, "A3"}, // inside a block: to its end16+ {"A3", 0, 1, "A6"}, // end of a block: to the next block17+ {"A4", 0, 1, "A6"}, // from a blank: to the next filled cell18+ {"A7", 0, 1, "A8192"}, // nothing ahead: sheet edge19+ {"A6", 0, -1, "A3"},20+ {"A1", 0, -1, "A1"}, // already at the edge21+ {"A1", 1, 0, "IV1"},22+ {"C1", -1, 0, "A1"},23+ }24+ for _, tt := range tests {25+ if got := s.Edge(at(tt.from), tt.dc, tt.dr); got != at(tt.want) {26+ t.Errorf("Edge(%s, %d, %d) = %s, want %s", tt.from, tt.dc, tt.dr, got, tt.want)27+ }28+ }29+}30+31+func TestUsedRangeAndStats(t *testing.T) {32+ s := New()33+ if _, ok := s.UsedRange(); ok {34+ t.Error("empty sheet has a used range")35+ }36+ s.Set(at("B2"), "10")37+ s.Set(at("B3"), "32")38+ s.Set(at("D1"), "label")39+ if r, _ := s.UsedRange(); r.String() != "A1:D3" {40+ t.Errorf("UsedRange = %s", r)41+ }42+ want := Stats{Sum: 42, Count: 3, Nums: 2}43+ if got := s.RangeStats(NewRect(at("A1"), at("D3"))); got != want {44+ t.Errorf("RangeStats small = %+v", got)45+ }46+ // Whole columns take the sparse path.47+ if got := s.RangeStats(NewRect(at("A1"), at("D8192"))); got != want {48+ t.Errorf("RangeStats large = %+v", got)49+ }50+}