Conditionally replacing the last list in a list of lists

Problem: I have a list of file names and their sizes, and I need to break them up into a list of lists, such that each sub-list is under 30mb in total size.

Solution: I modeled it as a reduction, where [[]] is the starting value and each step either adds a file to the last vector, or, if it won’t ‘fit’, adds a new vector into the outer vector with just the current file in it.

Using Specter for the former case eliminated some annoying initial condition checking and is just more concise.

(defn add-to-last-chunk
  [acc v]
  (sp/transform [sp/LAST] #(conj % v) acc))


(defn chunk-size
  [chnk]
  (reduce + (map second chnk)))


(defn chunker
  [acc [filepath size]]
  (let [current-chunk       (last acc)
        current-chunk-size  (chunk-size current-chunk)
        next-chunk-size     (+ size current-chunk-size)]
    (if (< max-chunk-size next-chunk-size)
      (conj acc [[filepath size]])
      (add-to-last-chunk acc [filepath size]))))

(def chunked
  (reduce chunker [[]] files-with-sizes))