; feed/aggregate — group-by / counting via key-reduce. Keys must be strings
; (dict keys), so composite keys (actor, day) are joined into one string.
;
; Requires: lib/feed/normalize.sx, lib/feed/stream.sx.

; group activities into a dict: key-string -> (list of activities), order-preserving
(define
  feed/group-by
  (fn
    (stream key-fn)
    (reduce
      (fn
        (g a)
        (let
          ((k (key-fn a)))
          (assoc g k (append (get g k (list)) (list a)))))
      {}
      (feed/items stream))))

; key-string -> count
(define
  feed/group-count
  (fn
    (stream key-fn)
    (reduce
      (fn
        (g a)
        (let
          ((k (key-fn a)))
          (assoc g k (+ (get g k 0) 1))))
      {}
      (feed/items stream))))

; --- composite keys ---------------------------------------------------------

(define feed/day (fn (at window) (floor (/ at window))))

; (actor, day-bucket) -> "actor#day"
(define
  feed/actor-day-key
  (fn
    (window)
    (fn
      (a)
      (string-append
        (get a :actor)
        "#"
        (number->string (feed/day (get a :at) window))))))

(define
  feed/by-actor-day
  (fn (stream window) (feed/group-count stream (feed/actor-day-key window))))

; per-actor activity counts
(define
  feed/actor-counts
  (fn (stream) (feed/group-count stream feed/actor)))

; per-object activity counts (engagement)
(define
  feed/object-counts
  (fn (stream) (feed/group-count stream feed/object)))
