[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.
pull/2176/head
Christopher Lam 4 weeks ago
parent 7825beed0b
commit dfe7295a08

@ -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 cells 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))))))

Loading…
Cancel
Save