Skip to content

Commit 069f24c

Browse files
committed
Add map-padded
1 parent 54f45ab commit 069f24c

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

src/medley/core.cljc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,3 +706,31 @@
706706
(if (next ks)
707707
(-> (get-in m (butlast ks)) (find (last ks)))
708708
(find m (first ks))))
709+
710+
(letfn [(first* [s v] (if s (first s) v))]
711+
(defn map-padded
712+
"Similar to `clojure.core/map`, except that it runs until all colls are
713+
exhausted, using `val` as the missing value for each exhausted coll."
714+
{:added "<<next>>"}
715+
#_{:clj-kondo/ignore [:unused-binding]}
716+
([f val c1] (map f c1))
717+
([f val c1 c2]
718+
(lazy-seq
719+
(let [s1 (seq c1) s2 (seq c2)]
720+
(when (or s1 s2)
721+
(cons (f (first* s1 val) (first* s2 val))
722+
(map-padded f val (rest s1) (rest s2)))))))
723+
([f val c1 c2 c3]
724+
(lazy-seq
725+
(let [s1 (seq c1) s2 (seq c2) s3 (seq c3)]
726+
(when (or s1 s2 s3)
727+
(cons (f (first* s1 val) (first* s2 val) (first* s3 val))
728+
(map-padded f val (rest s1) (rest s2) (rest s3)))))))
729+
([f val c1 c2 c3 & colls]
730+
(let [step (fn step [cs]
731+
(lazy-seq
732+
(let [ss (mapv seq cs)]
733+
(when (some identity ss)
734+
(cons (apply f (mapv #(first* % val) ss))
735+
(step (mapv rest ss)))))))]
736+
(step (conj colls c3 c2 c1))))))

test/medley/core_test.cljc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,3 +547,28 @@
547547
(is (= [:b 2] (m/find-in {:a {:b 2}} [:a :b])))
548548
(is (= [:b {:c 3}] (m/find-in {:a {:b {:c 3}}} [:a :b])))
549549
(is (= [:c 3] (m/find-in {:a {:b {:c 3}}} [:a :b :c]))))
550+
551+
(deftest test-map-padded
552+
(is (= (map + (range 3) (range 4) (range 5) (range 10))
553+
[0 4 8]
554+
(take 3 (m/map-padded + 0 (range 3) (range 4) (range 5) (range 10)))))
555+
(is (= [0 4 8 19 28 35 36 37 38 39]
556+
(m/map-padded + 10 (range 3) (range 4) (range 5) (range 10))))
557+
(is (= ()
558+
(m/map-padded + 10 () () ())))
559+
(testing "laziness"
560+
(let [state (volatile! [])]
561+
(is (= [0 4 8 19 28]
562+
(take 5 (m/map-padded (fn [a b c d]
563+
(vswap! state conj [a b c d])
564+
(+ a b c d))
565+
10
566+
(range 3) (range 4) (range 5) (range 10)))))
567+
(is (= [[0 0 0 0] [1 1 1 1] [2 2 2 2] [10 3 3 3] [10 10 4 4]]
568+
@state))))
569+
(testing "handles sequences with nils"
570+
(is (= [[nil 0 0 0] [nil 1 1 1] [nil 2 2 2]
571+
[:missing 3 3 3]
572+
[:missing :missing 4 4]
573+
[:missing :missing :missing 5]]
574+
(take 6 (m/map-padded vector :missing [nil nil nil] (range 4) (range 5) (range 10)))))))

0 commit comments

Comments
 (0)