From dfe7295a080fb13fa73ad350018cfb34ee35ec0e Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Mon, 26 Jan 2026 05:43:53 +0800 Subject: [PATCH] [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. --- gnucash/report/trep-engine.scm | 93 ++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 43 deletions(-) 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 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 (html-table)) (else (error "Unknown grid operation" msg))))))