Skip to content

Commit ecebaa9

Browse files
committed
Add sequence-padded
1 parent 069f24c commit ecebaa9

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

src/medley/core.cljc

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,10 +727,30 @@
727727
(cons (f (first* s1 val) (first* s2 val) (first* s3 val))
728728
(map-padded f val (rest s1) (rest s2) (rest s3)))))))
729729
([f val c1 c2 c3 & colls]
730-
(let [step (fn step [cs]
730+
(let [colls (vec (conj colls c3 c2 c1))
731+
step (fn step [cs]
731732
(lazy-seq
732733
(let [ss (mapv seq cs)]
733734
(when (some identity ss)
734735
(cons (apply f (mapv #(first* % val) ss))
735736
(step (mapv rest ss)))))))]
736-
(step (conj colls c3 c2 c1))))))
737+
(step colls))))
738+
739+
(defn sequence-padded
740+
"Similar to `clojure.core/sequence`, except that it runs until all colls are
741+
exhausted, using `val` as the missing value for each exhausted coll."
742+
#_{:clj-kondo/ignore [:unused-binding]}
743+
([xform val c1] (sequence xform c1))
744+
([xform val c1 & colls]
745+
(let [colls (vec (cons c1 colls))
746+
f (xform (completing #(cons %2 %1)))
747+
step (fn step [cs]
748+
(let [ss (mapv seq cs)]
749+
(if (some identity ss)
750+
(let [res (apply f nil (mapv #(first* % val) ss))]
751+
(cond (reduced? res) (f (deref res))
752+
(seq? res) (concat res (lazy-seq (step (mapv rest ss))))
753+
:else (step (mapv rest ss))))
754+
(f nil))))]
755+
(or (step colls)
756+
())))))

test/medley/core_test.cljc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,3 +572,29 @@
572572
[:missing :missing 4 4]
573573
[:missing :missing :missing 5]]
574574
(take 6 (m/map-padded vector :missing [nil nil nil] (range 4) (range 5) (range 10)))))))
575+
576+
(deftest test-sequence-padded
577+
(is (= (map + (range 3) (range 4) (range 5) (range 10))
578+
[0 4 8]
579+
(take 3 (m/sequence-padded (map +) 0 (range 3) (range 4) (range 5) (range 10)))))
580+
(is (= [0 4 8 19 28 35 36 37 38 39]
581+
(m/sequence-padded (map +) 10 (range 3) (range 4) (range 5) (range 10))))
582+
(is (= ()
583+
(m/sequence-padded (map +) 10 () () ())))
584+
(testing "laziness"
585+
(let [state (volatile! [])]
586+
(is (= [0 4 8 19 28]
587+
(take 5 (m/sequence-padded
588+
(map (fn [a b c d]
589+
(vswap! state conj [a b c d])
590+
(+ a b c d)))
591+
10
592+
(range 3) (range 4) (range 5) (range 10)))))
593+
(is (= [[0 0 0 0] [1 1 1 1] [2 2 2 2] [10 3 3 3] [10 10 4 4]]
594+
@state))))
595+
(testing "handles sequences with nils"
596+
(is (= [[nil 0 0 0] [nil 1 1 1] [nil 2 2 2]
597+
[:missing 3 3 3]
598+
[:missing :missing 4 4]
599+
[:missing :missing :missing 5]]
600+
(take 6 (m/sequence-padded (map vector) :missing [nil nil nil] (range 4) (range 5) (range 10)))))))

0 commit comments

Comments
 (0)