Let's imagine we want to compute two different functions on some given input. How can we do that with transducers?
For example, let's say we have these two transducers:
(def xf-dupl (map #(* 2 %)))
(def xf-inc (map inc))
Now, I would like some function f that takes a collection of transducers and returns a new transducer that combines them, as follows:
(into [] (f [xf-dupl xf-inc]) (range 5))
; => [[0 2 4 6 8] [1 2 3 4 5]]
There should probably be a very simple solution to this, but I cannot find it.
Note: I have tried with cgrand/xforms library's transjuxt, but there I get the following
(into [] (x/transjuxt {:a xf-dupl :b xf-inc}) (range 5))
; => [{:a 0 :b 1}]
Thanks for your help!
Using cgrand/xforms you can define f as
(defn f
[xfs]
(comp
(x/multiplex (zipmap (range) xfs))
(x/by-key (x/into []))
(map second)))
Calling f as you outlined in your question yields
user> (into [] (f [xf-dupl xf-inc]) (range 5))
[[0 2 4 6 8] [1 2 3 4 5]]
Related
In clojure, I am trying to accomplish the following logic:
Input:
{:a [11 22 33] :b [10 20 30]}, 2
Output:
{:a [11] :b [10 20 30 22 33]}
i.e. Move the last 2 elements from :a to :b
Is there a clojurish way for this operation?
Since you're effectively modifying both mappings in the map, it's probably easiest to explicitly deconstruct the map and just return the new map via a literal, using subvec and into for the vector manipulation:
(defn move [m n]
(let [{:keys [a b]} m
i (- (count a) n)
left (subvec a 0 i)
right (subvec a i)]
{:a left :b (into b right)}))
(move {:a [11 22 33] :b [10 20 30]} 2)
;;=> {:a [11], :b [10 20 30 22 33]}
As a bonus, this particular implementation is both very idiomatic and very fast.
Alternatively, using the split-at' function from here, you could write it like this:
(defn split-at' [n v]
[(subvec v 0 n) (subvec v n)])
(defn move [m n]
(let [{:keys [a b]} m
[left right] (split-at' (- (count a) n) a)]
{:a left :b (into b right)}))
First, using the sub-vec in the other answers will throw an IndexOutOfBoundsException when the number of elements to be moved is greater than the size of the collection.
Secondly, the destructuring, the way most have done here, couples the function to one specific data structure. This being, a map with keys :a and :b and values for these keys that are vectors. Now if you change one of the keys in the input, then you need to also change it in move function.
My solution follows:
(defn move [colla collb n]
(let [newb (into (into [] collb) (take-last n colla))
newa (into [] (drop-last n colla))]
[newa newb]))
This should work for any collection and will return vector of 2 vectors. My solution is far more reusable. Try:
(move (range 100000) (range 200000) 10000)
Edit:
Now you can use first and second to access the vector you need in the return.
I would do it just a little differently than Josh:
(defn tx-vals [ {:keys [a b]} num-to-move ]
{:a (drop-last num-to-move a)
:b (concat b (take-last num-to-move a)) } )
(tx-vals {:a [11 22 33], :b [10 20 30]} 2)
=> {:a (11), :b (10 20 30 22 33)}
Update
Sometimes it may be more convenient to use the clojure.core/split-at function as follows:
(defn tx-vals-2 [ {:keys [a b]} num-to-move ]
(let [ num-to-keep (- (count a) num-to-move)
[a-head, a-tail] (split-at num-to-keep a) ]
{ :a a-head
:b (concat b a-tail) } ))
If vectors are preferred on output (my favorite!), just do:
(defn tx-vals-3 [ {:keys [a b]} num-to-move ]
(let [ num-to-keep (- (count a) num-to-move)
[a-head, a-tail] (split-at num-to-keep a) ]
{:a (vec a-head)
:b (vec (concat b a-tail))} ))
to get the results:
(tx-vals-2 data 2) => {:a (11), :b (10 20 30 22 33)}
(tx-vals-3 data 2) => {:a [11], :b [10 20 30 22 33]}
(defn f [{:keys [a b]} n]
(let [last-n (take-last n a)]
{:a (into [] (take (- (count a) n) a))
:b (into b last-n)}))
(f {:a [11 22 33] :b [10 20 30]} 2)
=> {:a [11], :b [10 20 30 22 33]}
In case if the order of those items does not matter, here is my attempt:
(def m {:a [11 22 33] :b [10 20 30]})
(defn so-42476918 [{:keys [a b]} n]
(zipmap [:a :b] (map vec (split-at (- (count a) n) (concat a b)))))
(so-42476918 m 2)
gives:
{:a [11], :b [22 33 10 20 30]}
i would go with an approach, which differs a bit from the previous answers (well, technically it is the same, but it differs on the application-scale level).
First of all, transferring data between two collections is quite a frequent task, so it at least deserves some special utility function for that in your library:
(defn transfer [from to n & {:keys [get-from put-to]
:or {:get-from :start :put-to :end}}]
(let [f (if (= get-from :end)
(partial split-at (- (count from) n))
(comp reverse (partial split-at n)))
[from swap] (f from)]
[from (if (= put-to :start)
(concat swap to)
(concat to swap))]))
ok, it looks verbose, but it lets you transfer data from start/end of one collection to start/end of the other:
user> (transfer [1 2 3] [4 5 6] 2)
[(3) (4 5 6 1 2)]
user> (transfer [1 2 3] [4 5 6] 2 :get-from :end)
[(1) (4 5 6 2 3)]
user> (transfer [1 2 3] [4 5 6] 2 :put-to :start)
[(3) (1 2 4 5 6)]
user> (transfer [1 2 3] [4 5 6] 2 :get-from :end :put-to :start)
[(1) (2 3 4 5 6)]
So what's left, is to make your domain specific function on top of it:
(defn move [data n]
(let [[from to] (transfer (:a data) (:b data) n
:get-from :end
:put-to :end)]
(assoc data
:a (vec from)
:b (vec to))))
user> (move {:a [1 2 3 4 5] :b [10 20 30 40] :c [:x :y]} 3)
{:a [1 2], :b [10 20 30 40 3 4 5], :c [:x :y]}
I have a higher-order map-like function that returns a hashmap representing the application (input to output) of the function.
(defn map-application [f coll] (into {} (map #(vector % (f %)) coll)))
To be used thus:
(map-application str [1 2 3 4 5])
{1 "1", 2 "2", 3 "3", 4 "4", 5 "5"}
(map-application (partial * 10) [1 2 3 4 5])
{1 10, 2 20, 3 30, 4 40, 5 50}
Does this function already exist, or does this pattern have a recognised name?
I know it's only a one-liner, but looking at the constellation of related functions in clojure.core, this looks like the kind of thing that already exists.
I guess the term you are looking for is transducer.
https://clojure.org/reference/transducers
in fact the transducing variant would look almost like yours (the key difference is that coll argument is passed to into function not map), but it does it's job without any intermediate collections:
user> (defn into-map [f coll]
(into {} (map (juxt identity f)) coll))
#'user/into-map
user> (into-map inc [1 2 3])
;;=> {1 2, 2 3, 3 4}
this can also be done with the simple reduction, though it requires a bit more manual work:
user> (defn map-into-2 [f coll]
(reduce #(assoc %1 %2 (f %2)) {} coll))
#'user/map-into-2
user> (map-into-2 inc [1 2 3])
;;=> {1 2, 2 3, 3 4}
What you're describing is easily handled by the built-in zipmap function:
(defn map-application
[f coll]
(zipmap coll (map f coll)))
(map-application (partial * 10) [1 2 3 4 5])
=> {1 10, 2 20, 3 30, 4 40, 5 50}
Is there a more elegant way to have into work with single items and lists than the following (admittedly atrocious) function?
(defn into-1-or-more
[this-list list-or-single]
(into this-list (flatten (conj [] list-or-single))))
Which can handle either:
(into-1-or-more [1 2 3 4] 5)
;[1 2 3 4 5]
Or:
(into-1-or-more [1 2 3 4] [5 6 7 8])
;[1 2 3 4 5 6 7 8]
I am building a collection with reduce using into [] with results from functions in a list. However some of the functions return single items, and others return lists of items. Like:
(reduce #(into [] (% data)) [func-return-item func-return-list func-return-either])
Would the best solution be to just do the following instead?
(into [] (flatten (map #(% data) [func-return-item ...])
Although it would be more ideal to know for sure what return type you are getting, here is a simple answer:
(flatten [ curr-list (mystery-fn) ] )
Examples:
(flatten [[1 2 3] 9 ] )
;=> (1 2 3 9)
(flatten [[[1] 2 3] [4 5] 6 ] )
;=> (1 2 3 4 5 6)
You could wrap it into a function if you want, but it hardly seems necessary.
This transducer flattens sequential inputs, but only by one "level":
(defn maybe-cat [rf]
(let [catrf (cat rf)]
(fn
([] (rf))
([result] (rf result))
([result input]
(if (sequential? input)
(catrf result input)
(rf result input))))))
Example:
(into [] maybe-cat [[:foo] :bar [[:quux]]])
;= [:foo :bar [:quux]]
As this example demonstrates, this approach makes it possible to include sequential collections in the output (by wrapping them in an additional sequential layer – [[:quux]] produces [:quux] in the output).
So I have a list of functions and a list of data:
[fn1 fn2 fn3] [item1 item2 item3]
What can I do to apply each function to its corresponding data item:
[(fn1 item1) (fn2 item2) (fn3 item3)]
Example:
[str #(* 2 %) (partial inc)] [3 5 8]
=> ["3" 10 9]
You can use map
(map #(%1 %2) [str #(* 2 %) (partial inc)] [3 5 8])
("3" 10 9)
If you need a vector back, you can (apply vector ...)
(apply vector (map #(%1 %2) [str #(* 2 %) (partial inc)] [3 5 8]))
["3" 10 9]
Disclaimer: I don't know much Clojure, so there would probably be better ways to do this.
An alternative, not necessarily better:
user=> (for [[f x] (map vector [neg? pos? number?] [1 2 "foo"])]
#_=> (f x))
(false true false)
To make the map version suitable for varargs:
user=> (map (fn [f & args] (apply f args)) [+ - *] [1 2 3] [4 5 6] [7 8 9])
(12 -11 162)
I have a list [2 3 5] which I want to use to remove items from another list like [1 2 3 4 5], so that I get [1 4].
thanks
Try this:
(let [a [1 2 3 4 5]
b [2 3 5]]
(remove (set b) a))
which returns (1 4).
The remove function, by the way, takes a predicate and a collection, and returns a sequence of the elements that don't satisfy the predicate (a set, in this example).
user=> (use 'clojure.set)
nil
user=> (difference (set [1 2 3 4 5]) (set [2 3 5]))
#{1 4}
Reference:
http://clojure.org/data_structures#toc22
http://clojure.org/api#difference
You can do this yourself with something like:
(def a [2 3 5])
(def b [1 2 3 4 5])
(defn seq-contains?
[coll target] (some #(= target %) coll))
(filter #(not (seq-contains? a %)) b)
; (3 4 5)
A version based on the reducers library could be:
(require '[clojure.core.reducers :as r])
(defn seq-contains?
[coll target]
(some #(= target %) coll))
(defn my-remove
"remove values from seq b that are present in seq a"
[a b]
(into [] (r/filter #(not (seq-contains? b %)) a)))
(my-remove [1 2 3 4 5] [2 3 5] )
; [1 4]
EDIT Added seq-contains? code
Here is my take without using sets;
(defn my-diff-func [X Y]
(reduce #(remove (fn [x] (= x %2)) %1) X Y ))