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.
Related
I am just started clojure but I can't seem to figure out using/creating higher order functions.
I have partitioned a collection and I want to pass that into another function that will do something to the window of items. I am not sure how to go about doing this.
(def foo [:a :b :c :d :e])
(partition 3 1 foo)
;;=> ((:a :b :c) (:b :c :d) (:c :d :e))
(defn bar [start next end])
I think the basic outline would be.
(defn faz [collect]
(partition 3 1 collect)
;;maybe do here before passing
(bar stand next end)
)
I might be getting ahead of myself but I also see there are other functions like reduce and apply they can do something similar right? Although, most examples I see have it so they perform operations on two items at a time which are similar to (partition 2 1 foo)
You can do something like
(defn bar [start next end])
(defn faz [collect]
(let [partitions (partition 3 1 collect)]
(apply bar partitions)
))
or if you want to call bar directly, you can use destructuring
(defn bar [start next end])
(defn faz [collect]
(let [partitions (partition 3 1 collect)
[start next end] partitions]
(bar start next end)
))
Your question is general and there is more ways to achieve this, based on expected result and used function.
If you want to return sequence of results, use map and apply:
(defn results-for-triplets [collect]
(map #(apply + %) (partition 3 1 collect)))
(results-for-triplets [1 2 3 4 5])
=> (6 9 12)
For better readability, you can use ->> macro.
(defn results-for-triplets [collect]
(->> collect
(partition 3 1)
(map #(apply + %))))
(results-for-triplets [1 2 3 4 5])
=> (6 9 12)
You can avoid apply, if your function destructures passed sequence:
(defn sum3 [[a b c]]
(+ a b c))
(defn results-for-triplets [collect]
(->> collect
(partition 3 1)
(map sum3)))
(results-for-triplets [1 2 3 4 5])
=> (6 9 12)
If you want to call function for side effect and then return nil, use run!:
(defn print3 [[a b c]]
(println a b c))
(defn results-for-triplets [collect]
(->> collect
(partition 3 1)
(run! print3)))
(results-for-triplets [1 2 3 4 5])
1 2 3
2 3 4
3 4 5
=> nil
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)])
In Clojure I want change specific item(list) in list with other.
Here is my structure:
(def myLis '((3 3) (5 5) (5 5)))
(def som '(4 4))
I want change second element in myLis with som.
Result in myLis is '((3 3) (4 4) (5 5))
This is basic example. I have few hundred items in myList.
I try assoc and update-in but this not work on list.
When I try with assoc and update:
(update-in myLis [1] som)
(assoc myLis 1 som)
(assoc-in myLis [1] som)
got error like that:
clojure.lang.PersistentList cannot be cast to clojure.lang.Associative
How can quick change nth element in this structure (list of lists).
As pointed out in the clojure bible (Clojure Programming):
Because [lists] are linked lists, they do not support efficient random
access; thus, nth on a list will run in linear time (as opposed to
constant time when used with vectors, arrays, and so on), and get does
not support lists at all because doing so would not align with get’s
objective of sublinear efficiency.
So in order to replace an element of a list you will have to traverse all the elements up to it, thus running longer the further your item is in the list, and rebuild the list with the elements before it, the new item and all the elements after it (rest). Alternatively, turn the list into a vector, use update-in and back into a list if you absolutely have to use lists.
However, if you can, it would be worth seeing if you can use sequences in your code rather than lists, and thus you can interchangeably use vectors or other abstractions that are more efficient for the processing you are performing over them.
A trivial function that would meet the basics of your requirement with lists however, is:
(defn list-update-in [l i x]
(let [newlist (take i l)
newlist (concat newlist (list x))
newlist (concat newlist (drop (+ 1 i) l))]
newlist))
user> (list-update-in '((1 2) (2 3) (3 4)) 1 '(8 9))
((1 2) (8 9) (3 4))
There's no out of bounds checks on this
Adding an additional answer using loop/recur to realign the OP's own solution to be more lisp like.
(defn list-update-in-recur [l i x]
(loop [new-data [] old-list l]
(if (seq old-list)
(if (= (count new-data) i)
(recur (conj new-data x) (rest old-list))
(recur (conj new-data (first old-list)) (rest old-list)))
(apply list new-data))))
user> (list-update-in-recur '((1 2) (2 3) (3 4)) 1 '(8 9))
((1 2) (8 9) (3 4))
A few points to note:
It's written as a function, there are no 'def' values to set any global value. The final result is the return of the function (apply list new-data)
Arguments initialise the loop, the size of the growing list is used to determine if we want to replace the nth item or not (no index variables)
The passed in list becomes the initial old-list value which reduces in size each iteration, and the exit condition is simply if there are any more elements left in it using the test (seq old-list), which returns false/nil when it is empty.
Because we conj everything (which adds to start of the list) we reverse it to return the output. It now uses a vector to create the new sequence, and converts to a list as the last step instead of reversing a list
I've replaced nth with first and rest which are more efficient and don't have to traverse entire lists every iteration.
This is still very inefficient and only provided as a learning exercise.
You should normally use vectors like [1 2 3] in preference to lists like '(1 2 3) for most purposes. In Clojure, a list is normally used for a function call like (+ 1 2), while for data literals vectors normally used like [1 2 3].
Here is code showing 2 options that work.
Main code:
(ns clj.core
(:require
[tupelo.core :as t]
))
(t/refer-tupelo)
(def myLis [ [3 3] [5 5] [5 5] ] )
(def som [4 4] )
(spyx (assoc myLis 1 som))
(spyx (assoc-in myLis [1] som))
(defn -main [& args]
(println "-main"))
Result:
~/clj > lein run
(assoc myLis 1 som) => [[3 3] [4 4] [5 5]]
(assoc-in myLis [1] som) => [[3 3] [4 4] [5 5]]
You need this in project.clj to make the (spy ...) work:
:dependencies [
[tupelo "0.9.9"]
...
Update 2016-11-2:
If you really want to keep everything in a list, you can use replace-at from the Tupelo library. It works like this:
(def myLis '( (3 3) (5 5) (5 5) ) )
(def vec-1 [4 4] )
(def list-1 '(4 4) )
(spyx (t/replace-at myLis 1 vec-1 ))
(spyx (t/replace-at myLis 1 list-1))
(spyx (apply list (t/replace-at myLis 1 list-1)))
with result
> lein run
(t/replace-at myLis 1 vec-1) => [(3 3) [4 4] (5 5)]
(t/replace-at myLis 1 list-1) => [(3 3) (4 4) (5 5)]
(apply list (t/replace-at myLis 1 list-1)) => ((3 3) (4 4) (5 5))
The first 2 examples show that the new element can be anything, such as the vector [4 4] or the list (4 4). Also, notice that replace-at always returns a vector result. If you want the final result to be a list as well, you need to use (apply list <some-collection>).
My solution with lists:
(def newL '())
(def i 1)
(loop [k (- (count myLis) 1)]
(when (> k -1)
(cond
(= k i) (def newL (conj newL som))
:else (def newL (conj newL (nth myLis k)))
)
(recur (- k 1))
)
)
In Clojure, I have a map like this:
(def data {:a 1 :b 2 :c 3})
I want to sum all the elements and get 6 as a result. I know I should probably use reduce, but I'm at a loss at how to do it correctly.
There are two easy ways you can do this.
With reduce
(reduce + (vals data))
Or with apply
(apply + (vals data))
They are equivalent for associative functions.
I'd suggest that apply is more idiomatic, because + is already implemented via reduce.
That is, if we calculate (+ 1 2 3), the result is 6. So it's natural to ask why (+ (vals data)) isn't sufficient.
The result of (vals data) is the list (1 2 3). + sees this as a single argument and just returns that value... oops.
(+ (vals data))
=> (1 2 3)
apply works by essentially unpacking the list.
You are correct, you should reduce here. vals will get you the values you want to add up, then just reduce them over the addition function.
user=> (def data {:a 1 :b 2 :c 3})
#'user/data
user=> (vals data)
(3 2 1)
user=> (reduce + (vals data))
6
If I have a map, for example,
(def mymap { :b 1 :a 2 :d 3 :e 4 :f 5})
I can use vals to get a sequence of all of the values
(vals mymap)
;=> (1 2 3 4 5)
how do I get the sequence of values in my own custom order, to get for example
;=> (4 2 3 1 5)
what I eventually want to do is serialize the values to a string, doing something like this
(defn serialize [m sep] (apply str (concat (interpose sep (vals m)) ["\n"])))
(this example function was taken from the "serialize an input-map into string" post)
but I need to specify the order of the vals.
Maps are functions of their keys, so you can do this:
(map mymap [:e :a :d :b :f])
=> (4 2 3 1 5)
For 1.3 you can use the priority-map,
http://clojure.github.com/clojure-contrib/branch-master/priority-map-api.html
or you can use sort-by,
(let [m { 1 8 3 6 5 4 7 2}]
(println (map first (sort-by second m)))
(println (map first (sort-by first m))))
(7 5 3 1)
(1 3 5 7)
In case you want to sort the map depending on the keys, and then get the values,
Brian has an example on how to do this using sort-by
Or you can just implement your own sort comparator
I don't want to sort (although thanks for the sorting tips), I just want to specify the order
when I pull the values from the map.
I found a way to do it - destructuring the map.
(let [{:keys [a b d e f]} mymap]
(println e a d b f))