The idea is to take the collection functions add-one and minus-one and make them applicable to one single element:
(defn singlify [fun] (fn [x] (first (fun [x]))))
;; now you can do:
((singlify #'add-one) 3)
;; => 4
;; so #'add-one became a function applicable for one element only
;; instead to an entire sequence/collection
;; - well actually we just wrap around the argument x a vector and apply
;; the sequence-function on this `[x]` and then take the result out of the
;; result list/vec/seq by a `first` - so this is not the most performant solution.
;; however, we can now use the functions and get the single-element versions
;; of them without having to modify the original functions.
;; And this solution is generalize-able to all collection functions.
Now using the comments' helpful hints, which tell that juxt makes it possible to apply two different functions on a sequence while traversing just once through it,
we get
(map (juxt (singlify #'add-one) (singlify #'minus-one)) my-xs)
;; => ([2 0] [3 1] [4 2])
Using zipmap and the helpful lispy idiom (apply map #'<collector-function> <input-collection>) to transpose the result list, we can split them into a dict/map with the corresponding
keywords upfront:
(zipmap [:added :minused]
(apply map vector
(map (juxt (singlify #'add-one)
(singlify #'minus-one))
my-xs)))
;; => {:added [2 3 4], :minused [0 1 2]}
generalize as a function
We can generalize this as a function which takes a seq of keys, a seq of to-be-applied collection-functions and the to-be-once-only-traversed input seq/collection:
;; this helper function applies `juxt`
;; on the `singlify`-ed versions of the collection-functions:
(defn juxtify [funcs] (apply #'juxt (map #(singlify %) funcs)))
;; so the generalized function is:
(defn traverse-once [keys seq-funcs sq]
(zipmap keys (apply map vector (map (juxtify seq-funcs) sq))))
Using this function, the example case looks like this:
(traverse-once [:added :minused] [#'add-one #'minus-one] my-xs)
;; => {:added [2 3 4], :minused [0 1 2]}
We can now extend is as we want:
(traverse-once [:squared
:minused
:added]
[(fn [sq] (map #(* % %) sq))
#'minus-one
#'add-one]
my-xs)
;; => {:squared [1 4 9], :minused [0 1 2], :added [2 3 4]}
Voila!