I am currently trying to figure out a way to re-arrange entries within an existing map based, on a function apllied to its values, such as count:
(def map1 '{A: (1 2 3) B: (1 2) C: (5) } )
Sort by (count (vals map1))
=> {C: (5) B: (1 2) A: (1 2 3)}
Is there any straight-forward way to accomplish that?
Many thanks!
Use sort-by with anonymous function:
(def map1 '{:A (1 2 3) :B (1 2) :C (5)})
(sort-by (fn [e] (count (val e))) map1)
(sort-by #(count (val %)) map1)
or with composition:
(sort-by (comp count val) map1)
=> ([:C (5)] [:B (1 2)] [:A (1 2 3)])
Related
I am trying to write a function which, given a collection and a sequence of replacements, replaces any lists in the collection with the next replacement in the order they appear.
For example:
(substitute '(+ 1 (* 2 3) 4 (* 5 6) [:a :b]) => (+ 1 :a 4 :b)
(substitute '[1 (2 3 4) (5 6 7)] [:x :y :z]) => [1 :x :y]
(substitute '[(1 2 3) (4 5 6) (7 8 9)] [:x :y]) => [:x :y (7 8 9)]
Currently I have this:
(defn substitute
[form syms]
(if (seq form)
(lazy-seq
(if (and (not-empty syms) (list? (first form)))
(cons
(first syms)
(substitute (rest form) (rest syms)))
(cons
(first form)
(substitute (rest form) syms))))))
However I have two problems. First, I want the output to be the same type as form. I tried doing (into (empty form) (substitute form syms)) but this causes the output to be reversed when form is a list. Second, I am struggling to find a way to make this work on maps (I want to check for a list in both the key and value of each entry).
Any tips or pointers would be much appreciated. Thanks.
Here's an approach using clojure.walk/prewalk to traverse the form in order (pre-order) and using an atom to track what syms remain for substitution:
(defn substitute [form syms]
(let [syms' (atom syms)
depth (atom 0)]
(walk/prewalk
(fn [v]
(cond
(= 1 (swap! depth inc)) v ;; don't examine the input form itself
(list? v) (if-let [sym (first #syms')]
(do (swap! syms' rest)
sym)
v)
:else v))
form)))
The depth atom is to ensure we don't act on the first value which will be form itself, and if it were a list we wouldn't want to substitute the whole thing. At first I just checked (not= form v) but thought that could backfire if your form contains nested forms identical/equal to the outer form. I suspect there's a better way to accomplish this!
prewalk (and postwalk) also relieve you of having to worry about the type of collection you're walking i.e. lists will come out in the correct order.
(substitute '(+ 1 (* 2 3) 4 (* 5 6)) [:a :b])
=> (+ 1 :a 4 :b)
(substitute '[1 (2 3 4) (5 6 7)] [:x :y :z])
=> [1 :x :y]
(substitute '[(1 2 3) (4 5 6) (7 8 9)] [:x :y])
=> [:x :y (7 8 9)]
Using prewalk also allows this to work on maps w/o additional effort:
(substitute {:foo '(1 2 3) '(4 5 6) "hey"} [:a :b])
=> {:foo :a, :b "hey"}
You can also use prewalk-demo to illustrate how the form is traversed:
(walk/prewalk-demo {:foo '(1 2 3) '(4 5 6) "hey"})
Walked: {:foo (1 2 3), (4 5 6) "hey"}
Walked: [:foo (1 2 3)]
Walked: :foo
Walked: (1 2 3)
Walked: 1
Walked: 2
Walked: 3
Walked: [(4 5 6) "hey"]
Walked: (4 5 6)
Walked: 4
Walked: 5
Walked: 6
Walked: "hey"
Thanks to everyone who replied. I think I may have cracked it now.
(defn map-to-vec
[map]
(reduce-kv (fn [vec k v] (into vec [k v])) [] map))
(defn substitute-seq
[form syms]
(if (seq form)
(lazy-seq
(if (and (not-empty syms) (list? (first form)))
(cons
(first syms)
(substitute-seq (rest form) (rest syms)))
(cons
(first form)
(substitute-seq (rest form) syms))))))
(defn substitute
[form syms]
(cond
(list? form) (apply list (sub-syms-seq form syms))
(map? form)
(reduce
(fn [m [k v]] (conj m [k v]))
(empty form)
(partition 2 (sub-syms-seq (map-to-vec form) syms)))
(coll? form) (into (empty form) (sub-syms-seq form syms))))
I wrote a function map-to-vec which converts a map into a vector, such that {:a 1 :b 2 :c 3} becomes [:a 1 :b 2 :c 3] and added a helper function which ensures that the type of the output is the same as the input and, in the case of the input being a map, performs the main function (now called substitute-seq) on the map-to-veced map and then converts it back into a map again.
I'm sure there's still a better and more efficient way to do this but I suppose this works.
I am trying to combine 2 structures:
(def acc [[1]])
and
(def pairs '((2 4)))
I want the following result:
'((1 2) (1 4))
I have tried the following:
(map-indexed
(fn [idx pair]
(map (fn [itm]
(concat (nth acc idx) (vector itm))) pair)) pairs)
But this gives:
(((1 2) (1 4)))
I could call first but this falls apart as bigger lists are attempted.
For example if I had
(def acc '((1 2) (1 4)))
and
(def pairs '((5 1) (1 4)))
I want to the result to be:
'((1 2 5) (1 2 1) (1 4 1) (1 4 4))
You want an algorithm that takes two input sequences of sequences and does this:
Take one sequence from each input sequence so that you have two sequences, s1 and s2
For each elem in s2 produce a sequence of s1 with elem appended to it
Repeat 1. until one of the two input sequences has no more sequences left
In Clojure:
(mapcat (fn [s1 s2]
(map (fn [elem]
(conj s1 elem)) s2))
acc pairs)
Note: your algorithm requires appending to collections - it is better to use a collection type that supports fast access to the back (like a vector)
(def acc '([1 2] [1 4])) ;; notice the inner collections are vectors
(def pairs '([5 1] [1 4]))
(defn zipp
[c1 c2]
(mapcat (fn [c3 c4]
(map (partial conj c3) c4)) ;; change this line for lists!
c1
c2))
(zipp acc pairs)
;; => ([1 2 5] [1 2 1] [1 4 1] [1 4 4])
If you must work with list's, you can change the line marked above to:
(map (partial conj (into [] c3)) c4))
It's rather ugly, IMO.
When mapping over nested data structures, for is often simpler.
user>
(defn unfolder
[acc pairs]
(for [combination (map list acc pairs)
tail (second combination)]
(conj (vec (first combination)) tail)))
#'user/unfolder
user> (unfolder '((1 2) (1 4)) '((5 1) (1 4)))
([1 2 5] [1 2 1] [1 4 1] [1 4 4])
I wonder what is the idiomatic way of doing complicated data structures changes. Here is a hashmap that contains lists, i want to take one of those list and move some items to the other one:
input:
{ :a (1 2 3) :b (4 5) }
output:
{ :a (2 3) :b (4 5 1) }
"first element of :a is added as last to :b"
In practice I need such structure to represent game state like:
{ :first_player { :deck (2 3 4 5) :hand (6 1) :discard () }
:second_player { :deck (1 8 9 10) :hand (3) :discard (1 7) }
:board { :first_player_side (1 3) :second_player_side (7 9) }}
As u can see i will need to move cards ids from different lists to different lists in various hashmaps (play from hand to board, move from board to discard pile etc). I just wonder how to make it in simple/readable way.
Thanks for answers.
Clojure has an update-in function, designed for doing complicated data structures changes:
(defn magic [{:keys [a] :as m}]
(-> m
(update-in [:a] rest)
(update-in [:b] concat (take 1 a))))
Or more general solution:
(defn move-els [m from to numel]
(let [els (take numel (get m from))]
(-> m
(update-in [from] (partial drop numel))
(update-in [to] concat els))))
Try it:
=> (move-els { :a '(1 2 3) :c "foo" :b '(4 5) } :a :b 2)
{:a (3), :c "foo", :b (4 5 1 2)}
Are you looking for map destructuring?
(defn frob [{:keys [a b]}]
{:a (rest a) :b (conj b (first a))})
This doesn't do exactly what you want, of course, because that wouldn't be much fun.
See http://blog.jayfields.com/2010/07/clojure-destructuring.html for a good quick overview of restructuring.
Another restless night trying to figure out how to do this. Given the nested data structure which I've reformatted for readability looking for malformed declaration:
(def tm (list
{:a 1 :b 2 :c 3 :child (list
{:a 4 :b 5 :c 6 :child (list
{:a 40 :b 50 :c 60 :child (list {:a nil :b nil :c nil})})}
{:a 70 :b 80 :c 90 :child (list {:a nil :b nil :c nil})})}
{:a 400 :b 500 :c 600 :child (list {:a nil :b nil :c nil})}
{:a 7 :b 8 :c 9 :child (list
{:a 10 :b 11 :c 12 :child (list {:a nil :b nil :c nil})})})
)
And the traversal can be seen in dg123 example below
I'm getting the following:
=>(roll-down tm :a)
((1 4 40) (1 70) (7 10))
But was expecting:
((1 4 40) (1 70) (400) (7 10))
Here's a zippers based solution
(require '[clojure.zip :as z])
(defn root-to-leaf-paths [t]
(loop [t t paths []]
(cond
(z/end? t) paths
(z/branch? t) (recur (z/next t) paths)
:leaf (recur (z/next t) (conj paths (z/path t))))))
(defn roll-down [d kw]
(->> (z/zipper :child :child nil {:child d})
(root-to-leaf-paths)
(map (comp rest (partial map kw)))))
Note: Your tm isn't a tree but a list of trees. So, here we first turn this structure into a tree by placing the list of trees under one parent root {:child d}. Then we drop this from the resulting paths (comp rest ...).
Examples
(roll-down tm :a)
;=> ((1 4 40) (1 70) (400) (7 10))
(roll-down tm :b)
;=> ((2 5 50) (2 80) (500) (8 11))
(roll-down tm :c)
;=> ((3 6 60) (3 90) (600) (9 12))
a short version use mapcat:
(defn roll-down [g k]
(mapcat
#(if-let [c (:child %)]
(map (fn [x] (conj x (k %))) (roll-down c k))
[[(k %)]])
g))
-> (roll-down tm :a)
([nil 40 4 1] [nil 70 1] [nil 400] [nil 10 7])
This solution uses a depth first traverse and also treat nil as a path. If you like you can just do a remove and reverse on the result.
Here's a more functional approach that doesn't use mutable state:
(defn roll-down-helper [c kw]
(when-let [root-val (kw c)]
(cons root-val
(filter (comp not nil?)
(for [node (:child c)]
(roll-down-helper node kw))))))
(defn get-paths [[x & ys]]
(if (empty? ys)
(list (list x))
(for [y ys]
(flatten (cons x y)))))
(defn roll-down [c kw]
(apply concat
(map (comp get-paths #(roll-down-helper % kw)) c)))
Then you can do the following:
user> (roll-down tm :a)
((1 4 40) (1 70) (400) (7 10))
user> (roll-down tm :b)
((2 5 50) (2 80) (500) (8 11))
user> (roll-down tm :c)
((3 6 60) (3 90) (600) (9 12))
You could simply map your extractor onto your data
(defn get-as [tm]
(let [results (atom [])]
(clojure.walk/prewalk
#(do (if-let [uid (:a %)]
(swap! results conj :a (:a %)))
%)
tm)
#results))
(map get-as tm) ; => ([:a 1 :a 4] [:a 7 :a 10])
I need to traverse a list and do some calculations with every element and the elements excluding that element. For example, having a list (1 2 3 1), I need pairs (1) (2 3 1), (2) (1 3 1), (3) (1 2 1) and (1) (2 3 1).
(...) with every element and the elements excluding that element.
With every element sounds like a map. Excluding that element sounds like a filter. Let's start with the latter.
user=> (filter #(not= % 1) '(1 2 3))
(2 3)
Great. Now let's try to map it over all elements.
user=> (let [coll '(1 2 3)] (map (fn [elem] (filter #(not= % elem) coll)) coll))
((2 3) (1 3) (1 2))
Creating actual pairs is left as an exercise for the reader. Hint: modify the closure used in map.
Keep in mind that the solution suggested above should work fine for short lists but it has a complexity of O(n²). Another issue is the fact that collections with duplicates aren't handled correctly.
Let's take a recursive approach instead. We'll base the recursion on loop and recur.
(defn pairs [coll]
(loop [ahead coll behind [] answer []]
(if (empty? ahead)
answer
(let [[current & remaining] ahead]
(recur remaining
(conj behind current)
(conj answer [(list current)
(concat behind remaining)]))))))
A trivial example:
user=> (pairs '(1 2 3))
[[(1) (2 3)] [(2) (1 3)] [(3) (1 2)]]
A vector with duplicates:
user=> (pairs [1 5 6 5])
[[(1) (5 6 5)] [(5) (1 6 5)] [(6) (1 5 5)] [(5) (1 5 6)]]
Seems a job for a list comprehension:
(defn gimme-pairs [coll]
(for [x coll]
[(list x) (remove #{x} coll)]))
user=> (gimme-pairs [1 2 3])
([(1) (2 3)] [(2) (1 3)] [(3) (1 2)])
I would actually skip creating a list for the single element, which would make the code even easier:
(defn gimme-pairs [coll]
(for [x coll]
[x (remove #{x} coll)]))
user=> (gimme-pairs [1 2 3])
([1 (2 3)] [2 (1 3)] [3 (1 2)])
If you need to keep duplicates then you can use an indexed collection:
(defn gimme-pairs [coll]
(let [indexed (map-indexed vector coll)
remove-index (partial map second)]
(for [[i x] indexed]
[x (remove-index (remove #{[i x]} indexed))])))
user=> (gimme-pairs [1 2 3 1])
([1 (2 3 1)] [2 (1 3 1)] [3 (1 2 1)] [1 (1 2 3)])
(def l [1 2 3])
(first (reduce (fn [[res ll] e]
[(conj res [(list e) (rest ll)])
(conj (vec (rest ll)) e)])
[[] l] l))
=> [[(1) (2 3)] [(2) (3 1)] [(3) (1 2)]]