This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-03-15
Channels
- # announcements (10)
- # asami (5)
- # babashka (49)
- # babashka-sci-dev (8)
- # beginners (25)
- # calva (98)
- # cider (2)
- # clj-kondo (22)
- # clojure (32)
- # clojure-dev (12)
- # clojure-europe (32)
- # clojure-nl (3)
- # clojure-spec (3)
- # clojure-uk (10)
- # clojurescript (12)
- # community-development (1)
- # conjure (71)
- # cursive (7)
- # datalog (6)
- # events (2)
- # figwheel-main (2)
- # fulcro (4)
- # jobs (2)
- # kaocha (3)
- # lsp (43)
- # membrane (12)
- # missionary (9)
- # off-topic (61)
- # pathom (7)
- # polylith (2)
- # reagent (38)
- # remote-jobs (4)
- # shadow-cljs (17)
- # specter (1)
- # tools-deps (38)
- # vim (51)
- # web-security (5)
If I expect a vector in a particular sequence, but know that the vector I'm given might have some gaps in it, what's the easiest way to "fill in the blanks"? I'm thinking a combination of cycle
, map-indexed
, and reduce
.
Well, Clojure vectors can't actually have gaps. There can be nil
s in there, which could be considered gaps, but they are still values.
Can you provide a couple of example inputs and expected outputs for this "fill in the blanks" function? It isn't clear to me what you want.
I mean logical gaps:
data [1 2 3 4 5
1 3 4
1 2 4 5]
ids [1 2 3 4 5]
exp-count (* 5 3)
exp (take exp-count (cycle ids))]
Does that make sense?Something like this, but less horrible 🙂
(let [data [1 2 3 4 5
1 3 4
1 2 4 5]
ids [1 2 3 4 5]
exp-count (* 5 3)
exp (take exp-count (cycle ids))]
(loop [data-idx 0
exp-idx 0
result []]
(cond
(= exp-idx exp-count) result
(= (nth data data-idx)
(nth exp exp-idx))
(recur (inc data-idx)
(inc exp-idx)
(conj result (nth data data-idx)))
:else (recur data-idx
(inc exp-idx)
(conj result :x #_(nth exp exp-idx)))
; => [1 2 3 4 5 1 :x 3 4 :x 1 2 :x 4 5]
I see. I came-up with something more horrible 😃
(defn- follow
"returns the common-prefix length of actual and expected"
[actual expected]
(->> (map vector actual expected)
(take-while (fn [[x y]] (= x y)))
(count)))
(defn fill-gaps
"fills-in gaps in actual to make it a prefix of expected.
starts with a gap (empty vector if no gap necessary at the beginning)"
[actual expected]
{:post [(->> (apply concat %)
(map vector expected)
(every? (fn [[x y]] (= x y))))]}
(loop [[x :as actual] actual
expected expected
ret []]
(if (empty? actual)
ret
(let [[gap expected] (split-with (complement #{x}) expected)
n (follow actual expected)]
(recur
(drop n actual)
(drop n expected)
(conj ret (vec gap) (vec (take n actual))))))))
With the example you gave:
(fill-gaps [1 2 3 4 5 1 3 4 1 4 5] (cycle [1 2 3 4 5]))
; => [[] [1 2 3 4 5 1] [2] [3 4] [5] [1] [2 3] [4 5]]
This fn "fills" the gaps using the elements "expected" in said gaps. (I put an invariant in the post-condition)heh nice 🙂 I feel like someone is going to come in here and solve it with a single function call.
((fn f [the-seq filler-a filler-b]
(when (seq the-seq)
(if (seq filler-a)
(if (= (first the-seq) (first filler-a))
(lazy-seq (cons (first the-seq)
(f (rest the-seq)
(rest filler-a)
(conj filler-b (first filler-a)))))
(lazy-seq (cons (first filler-a)
(f the-seq
(rest filler-a)
(conj filler-b (first filler-a))))))
(recur the-seq filler-b []))))
[1 2 3 4 5 1 3 4 1 4 5]
[1 2 3 4 5]
[])
((fn f [the-seq filler]
(when (seq the-seq)
(if (= (first the-seq) (first filler))
(cons (first the-seq) (f (rest the-seq) (rest filler)))
(cons (first filler) (f the-seq (rest filler))))))
[1 2 3 4 5 1 3 4 1 4 5]
(cycle [1 2 3 4 5]))
not really a sort, a deduping merge@U0NCTKEV8 thanks, I like that. I'll probably put the cycle
inside somewhere I can just pass in the-seq and the-order, but that's approx. what I need.
maybe because of degenerate example, but if you’re always filling in the gaps, then don’t you just know the final answer?
but until you look over the input to find gaps, you don't know n
, and finding gaps is basically the same procedure as filling them in
Yeah, it is a simplified example. In the actual case it's a vector of maps, with each map having an ID and some other set of keys. If there's a gap in the IDs, I need to fill it in with a map that has the right ID and some computed values. The example is just the shortest representation I could think of.
then perhaps: (merge map-of-defaults-over-all-ids map-of-custom-ids-with-unique-values)
Yep, that's definitely a part of it but first the missing maps have to be identified.
I haven't looked in here forever, and this was last week, but I've just looked now, and it's a cute puzzle. My approach was similar to @U0NCTKEV8, though I think I was influenced by @UPWHQK562 with the use of reduce
. So I came up with this:
(defn fill-in
[sparse-seq replace-val cycle-seq]
(reduce
(fn [[result [f & r :as c]] x]
(if-not f
(reduced result)
(if (= f x)
[(conj result x) r]
[(conj result :x) c])))
[[] sparse-seq]
(cycle (cycle-seq)))
(fill-in [1 2 3 4 5 1 3 4 1 4 5] :x (range 1 6))
It's just a merge, but I'm disappointed at how long this is. I feel like there should be something simpler that uses core, and not just a loop:
(defn fill-in
[sparse-seq replace-val cycle-seq]
(loop [r []
[fs & rs :as s] sparse-seq
[fc & rc] (cycle cycle-seq)]
(if-not (seq s)
r
(if (= fs fc)
(recur (conj r fs) rs rc)
(recur (conj r :x) s rc)))))
That's simple, but again, it feels like there is something simpler and more elegant. It's eluding me though 😕@U051N6TTC thanks for the input 🙂 Looks good, but not the one-liner I was hoping for 😛