gnucash stable: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Tue Jan 27 10:33:56 EST 2026


Updated	 via  https://github.com/Gnucash/gnucash/commit/dfe7295a (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7825beed (commit)
	from  https://github.com/Gnucash/gnucash/commit/f0856f7f (commit)



commit dfe7295a080fb13fa73ad350018cfb34ee35ec0e
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Mon Jan 26 05:43:53 2026 +0800

    [trep-engine.scm] grid: replace cell data with nested hash tables
    
    Replace the list-based grid-cell storage with nested hash tables keyed
    by row and column, with per-cell commodity collectors.
    
    Eliminates repeated list traversals and duplicate filtering when
    building and rendering the subtotal table, reducing gc load.

diff --git a/gnucash/report/trep-engine.scm b/gnucash/report/trep-engine.scm
index 5858a0f45a..d85c592c51 100644
--- a/gnucash/report/trep-engine.scm
+++ b/gnucash/report/trep-engine.scm
@@ -56,6 +56,7 @@
              (gnucash report html-text))
 (use-modules (srfi srfi-11))
 (use-modules (srfi srfi-1))
+(use-modules (srfi srfi-2))
 (use-modules (srfi srfi-9))
 (use-modules (srfi srfi-26))
 (use-modules (ice-9 match))
@@ -511,6 +512,17 @@ in the Options panel."))
 
 (define gnc:lists->csv lists->csv)
 
+;; returns a list of hash keys
+(define (hash-keys hash)
+  (hash-fold (lambda (k _ p) (cons k p)) '() hash))
+
+;; mimics c++ std::map::operator[] - return the value corresponding to key,
+;; creating a new value if there's no existing one
+(define (hash-ref! hash key constructor)
+  (or (hash-ref hash key)
+      (let ((new-value (constructor)))
+        (hash-set! hash key new-value)
+        new-value)))
 
 ;;
 ;; Default Transaction Report
@@ -2072,49 +2084,50 @@ be excluded from periodic reporting.")
 
 (define (make-grid)
 
-  (define-record-type :grid-cell
-    (make-grid-cell row col datum)
-    grid-cell?
-    (row get-grid-row)
-    (col get-grid-col)
-    (datum get-grid-datum))
-
-  (let ((cells '()))
+  ;; cells : row-key → col-key → cell
+  ;; Primary data store. Each cell is a commodity collector.
+  ;;
+  ;; rows  : row-key → commodity → #t
+  ;; Tracks which commodities appear in each row so we know which
+  ;; sub-rows to render (one per commodity).
+  ;;
+  ;; cols  : col-key → #t
+  ;; Set of all columns that have received any data.
 
-    (define (cell-match? cell row col)
-      (and (or (not row) (equal? row (get-grid-row cell)))
-           (or (not col) (equal? col (get-grid-col cell)))))
+  (let ((cells (make-hash-table))
+        (rows (make-hash-table))
+        (cols (make-hash-table)))
 
-    (define (grid-get row col)
-      (filter (cut cell-match? <> row col) cells))
+    (define (grid-get row col commodity)
+      (and-let* ((row-ht (hash-ref cells row))
+                 (coll (hash-ref row-ht col)))
+        (coll 'getmonetary-strict commodity #f)))
 
     (define (grid-rows)
-      (delete-duplicates (map get-grid-row cells)))
+      (hash-keys rows))
 
     (define (grid-cols)
-      (delete-duplicates (map get-grid-col cells)))
+      (hash-keys cols))
 
+    ;; Add a list of <gnc:monetary> values into a single grid cell.
+    ;;  - Ensures the row and column exist and have commodity collector
+    ;;  - Merges amounts into the cell’s commodity collector
+    ;;  - Records commodities in this row
     (define (grid-add row col data)
-      (let lp ((rest cells) (rv '()) (added? #f))
-        (match rest
-          (() (set! cells (if added? rv (cons (make-grid-cell row col data) rv))))
-          (((? (cut cell-match? <> row col) this) . more)
-           (let* ((coll (apply gnc:monetaries-add (append (get-grid-datum this) data)))
-                  (new-cell (make-grid-cell row col (coll 'format gnc:make-gnc-monetary #f))))
-             (lp more (cons new-cell rv) #t)))
-          ((this . more) (lp more (cons this rv) added?)))))
+      (hash-set! cols col #t)
+      (let* ((cells-row-ht (hash-ref! cells row make-hash-table))
+             (cells-row-col-data (hash-ref! cells-row-ht col gnc:make-commodity-collector))
+             (rows-ht (hash-ref! rows row make-hash-table)))
+        (for-each
+         (lambda (mon)
+           (let ((comm (gnc:gnc-monetary-commodity mon)) (amt (gnc:gnc-monetary-amount mon)))
+             (cells-row-col-data 'add comm amt)
+             (hash-set! rows-ht comm #t)))
+         data)))
 
     (define (row->commodities row)
-      (sort!
-       (fold (lambda (cell acc)
-               (fold (lambda (mon acc2)
-                       (let ((comm (gnc:gnc-monetary-commodity mon)))
-                         (if (member comm acc2) acc2 (cons comm acc2))))
-                     acc
-                     (get-grid-datum cell)))
-             '()
-             (grid-get row #f))
-       (lambda (a b) (< (gnc-commodity-compare a b) 0))))
+      (sort! (hash-keys (hash-ref rows row (make-hash-table)))
+             (lambda (a b) (< (gnc-commodity-compare a b) 0))))
 
     (define (grid->html-table)
       (define (<? a b)
@@ -2137,14 +2150,8 @@ be excluded from periodic reporting.")
                  (/ amount divisor) scu GNC-HOW-RND-ROUND)))))
 
       (define (make-table-cell row col commodity divisor)
-        (let ((cell (grid-get row col)))
-          (if (null? cell) ""
-              (gnc:make-html-table-cell/markup
-               "number-cell"
-               (monetary-div
-                (find (lambda (mon) (equal? commodity (gnc:gnc-monetary-commodity mon)))
-                      (get-grid-datum (car cell)))
-                divisor)))))
+        (and-let* ((cell (grid-get row col commodity)))
+          (gnc:make-html-table-cell/markup "number-cell" (monetary-div cell divisor))))
 
       (define (make-row row commodity first?)
         (append
@@ -2184,10 +2191,10 @@ be excluded from periodic reporting.")
     (lambda (msg . args)
       (case msg
         ((add)        (apply grid-add args))
-        ;; ((get)        (apply grid-get args))      ;; (grid 'get row col)
+        ;; ((get)        (apply grid-get args))      ;; (grid 'get row col commodity)
         ;; ((rows)       (grid-rows))
         ;; ((cols)       (grid-cols))
-        ;; ((clear)      (set! cells '()))
+        ;; ((clear)      (for-each hash-clear! (list cells rows cols)))
         ((get-html)   (grid->html-table))
         (else (error "Unknown grid operation" msg))))))
 

commit 7825beed0b351e48dbde077b4614f115981279b8
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Jan 27 21:06:42 2026 +0800

    [report-utilities.scm] gnc:make-commodity-collector 'getmonetary-strict
    
    will find monetary with desired commodity or #f if does not
    exist. this is similar to 'getmonetary but does not return a
    zero-amount gnc:monetary object.

diff --git a/gnucash/report/report-utilities.scm b/gnucash/report/report-utilities.scm
index 68160ffdeb..b8bac38efe 100644
--- a/gnucash/report/report-utilities.scm
+++ b/gnucash/report/report-utilities.scm
@@ -20,6 +20,7 @@
 (define-module (gnucash report report-utilities))
 
 (use-modules (srfi srfi-1))
+(use-modules (srfi srfi-2))
 (use-modules (srfi srfi-13))
 (use-modules (srfi srfi-26))
 (use-modules (ice-9 format))
@@ -378,6 +379,13 @@
              (total (if pair ((cadr pair) 'total #f) 0)))
         (gnc:make-gnc-monetary c (if sign? (- total) total))))
 
+    ;; same as getmonetary, if the commodity doesn't
+    ;; exist in commodity-collector, returns #f
+    (define (getmonetary-strict c sign?)
+      (and-let* ((pair (assoc c commoditylist))
+                 (total ((cadr pair) 'total #f)))
+        (gnc:make-gnc-monetary c (if sign? (- total) total))))
+
     (define (not-zero? l) (not (zero? ((cadr l) 'total #f))))
 
     ;; Dispatch function
@@ -392,6 +400,7 @@
         ((reset) (set! commoditylist '()))
         ((getpair) (getpair commodity amount))
         ((getmonetary) (getmonetary commodity amount))
+        ((getmonetary-strict) (getmonetary-strict commodity amount))
         ((remove-zeros) (set! commoditylist (filter not-zero? commoditylist)))
         ((list) commoditylist) ; this one is only for internal use
         (else (gnc:warn "bad commodity-collector action: " action))))))



Summary of changes:
 gnucash/report/report-utilities.scm |  9 ++++
 gnucash/report/trep-engine.scm      | 93 ++++++++++++++++++++-----------------
 2 files changed, 59 insertions(+), 43 deletions(-)



More information about the gnucash-changes mailing list