What's the most idiomatic way of editing or updating a single element of a lazy sequence?
There's no built-in function for modifying a single element of a sequence/list, but map-indexed
is probably the closest thing. It's not an efficient operation for lists. Assuming you don't need laziness, I'd pour the sequence into a vector, which is what mapv
does i.e. (into [] (map f coll))
. Depending on how you use your modified sequence, it may be just as performant to vectorize it and modify.
You could write a function using map-indexed
to do something similar and lazy:
(defn assoc-seq [s i v]
(map-indexed (fn [j x] (if (= i j) v x)) s))
Or if you want to do this work in one pass lazily without vector-izing, you can also use a transducer:
(sequence
(comp
(map inc)
(map-indexed (fn [j x] (if (= 0 j) 666 x))))
[1 2 3])
Realizing your use case is to only modify the first item in a lazy sequence, then you can do something simpler while preserving laziness:
(concat [666] (rest s))
Update re: comment on optimization: leetwinski's assoc-at
function is ~8ms faster when updating the 500,000th element in a 1,000,000 element lazy sequence, so you should use his answer if you're looking to squeeze every bit of performance out of an inherently inefficient operation:
(def big-lazy (range 1e6))
(crit/bench
(last (assoc-at big-lazy 500000 666)))
Evaluation count : 1080 in 60 samples of 18 calls.
Execution time mean : 51.567317 ms
Execution time std-deviation : 4.947684 ms
Execution time lower quantile : 47.038877 ms ( 2.5%)
Execution time upper quantile : 65.604790 ms (97.5%)
Overhead used : 1.662189 ns
Found 6 outliers in 60 samples (10.0000 %)
low-severe 4 (6.6667 %)
low-mild 2 (3.3333 %)
Variance from outliers : 68.6139 % Variance is severely inflated by outliers
=> nil
(crit/bench
(last (assoc-seq big-lazy 500000 666)))
Evaluation count : 1140 in 60 samples of 19 calls.
Execution time mean : 59.553335 ms
Execution time std-deviation : 4.507430 ms
Execution time lower quantile : 54.450115 ms ( 2.5%)
Execution time upper quantile : 69.288104 ms (97.5%)
Overhead used : 1.662189 ns
Found 4 outliers in 60 samples (6.6667 %)
low-severe 4 (6.6667 %)
Variance from outliers : 56.7865 % Variance is severely inflated by outliers
=> nil
The assoc-at
version is 2-3x faster when updating the first item in a large lazy sequence, but it's no faster than (last (concat [666] (rest big-lazy)))
.