(conj {:a 1 :b 2 :c 3}
{:a 4 :d 5})
;;=> {:a 4, :b 2, :c 3, :d 5}
(merge {:a 1 :b 2 :c 3}
{:a 4 :d 5})
;;=> {:a 4, :b 2, :c 3, :d 5}
I found conj can 'merge' 2 maps. Then, what is the purpose of the merge function?The implementation of merge is enlightening to this point. Compare for instance their behavior handling an absence of arguments or nil.
(conj {:a 1 :b 2 :c 3} nil)
;;=> {:a 1, :b 2, :c 3}
(merge {:a 1 :b 2 :c 3} nil)
;;=> {:a 1, :b 2, :c 3}
(conj nil nil)
;;=> (nil)
(merge nil nil)
;;=> nil
This shows the difference between nil and nil. I learned for the first time that you can concatenate to nil.
Does this mean that merge behaves intuitively correctly, particularly with maps?In conj, nil seems to be treated equivalently to an empty list (skipping the lesson that nil isn't actually an empty list). π€―
Basically yeah. See also e.g. (conj) or (conj nil {}). Multipurpose tool versus specialized affordances.
okey, thanks!
merge is also much more clear when you have stuff like (merge a x). (conj a x) doesn't tell as much.
(conj nil nil) - this is an example nil punning in Clojure: https://ericnormand.me/article/nil-punning
Not just printing: public abstract class AMapEntry extends APersistentVector
(ins)user=> (conj {:a 0} [:b 1])
{:a 0, :b 1}
(ins)user=> (merge {:a 0} [:b 1])
{:a 0, :b 1}
these both work, but I'd argue that only the first one is idiomatic(it's common to represent a map entry as a vector literal, because that's also how they natively print)
(ins)user=> (first {:a 0})
[:a 0]
(ins)user=> (type *1)
clojure.lang.MapEntry
(ins)user=> (vector? (first {:a 0}))
trueHuh, I'm surprised the second doesn't result in {:a 0, 0 :b, 1 1}.
I want to perform side-effects like one would with doseq but I'd rather use eduction as it is more efficient. I've had some pearl clutching at this code:
(reduce (fn [_ [k v]]
(side-effect-fn k v))
[] (eduction coll))
Assume coll is a big collection that comes from a database and has a function pipeline applied.
This works well but, since I would like to retain my place in polite society π
, is there a better option that retains the efficiency / single pass effect that eduction brings? Thanks in advance!Yes, I'm looking forward to my own π€¦π»
i think run! is exactly this
You got me! π€¦π»
and one thing iβve seen is if coll is essentially cursor based results make sure you donβt risk a timeout on a connection held open
thanks, it's all good on that front
(but iβm a big fan of this pattern, but thatβs the one thing that can get annoying when it runs for > 5 min in prod and it dies with a βThis resultset is closedβ error
Yeah I'm using it on a local RocksDB on a Kafka partition. We get passed an Iterable that we can safely access like this:
with-open [iter (.prefixScan store key-prefix (StringSerializer.))]
but if the processing window blows up (analogous to your connection timeout example), we have bigger problems!thanks for the tidy up tip and I can rejoin polite society once more ππΌ
you never left π
you might be interested in this idea too https://ask.clojure.org/index.php/13153/could-gain-another-arity-cover-common-case-feeding-eduction
run! is just a wrapper around reduce. Instead of reduce+`eduction`, you can use transduce.
In this case, run! is good for indicating side effects ... I'm explicitly not returning anything
reduce-kav would be nicer?
For those interested in the feature @alexmiller mentioned, please do upvote it in Ask Clojure. More votes = higher priority, at least for anything viable.