Counted values in map from maps - clojure

A still-learning clojure-newbie (me) got a list of maps.
Each map contains one account number and other information
(e.g. ({:account 123, :type "PK", :end "01.01.2013", ...} {:account 456 :type "GK" :end "01.07.2016", ...})
Now I need a function that sequentially puts a increasing number and the account number
( like {1, 123, 2, 456 etc}). And I didn't get it no matter what I tried.
I once learned Delphi, and it would be there like
for i :=1 to (count MYMAP)
do (put-in-a-list i AND i-th account number in the list)
inc i
Due to some restrictions I am not allowed to use functions out of the core and also I must not use "use", "ns", "require", "cycle", "time", "loop", "while", "defn", "defstruct", "defmacro", "def", "defn", "doall", "dorun", "eval", "read-string", "repeatedly", "repeat", "iterate", "import", "slurp", "spit" .
And - please excuse me if there is any bad english - it's not usual for me to ask such questions in english.

For lazy sequence of natural numbers interspersed with account numbers, you can try something like the following:
(interleave ; splices together the following sequences
(map inc (range)) ; an infinite sequence of numbers starting at 1
(map :account ; gets account numbers out of maps
[{:account 123, :type "PK", :end "01.01.2013", ...}, ...])) ; your accounts
However, the {} notation in your example ({1, 123, 2, 456 etc}) suggests you might be more interested in a map. In that case, you can use zipmap:
(zipmap ; makes a map with keys from first sequence to values from the second
(map inc (range))
(map :account
[{:account 123, :type "PK", :end "01.01.2013", ...}, ...]))

map-indexed will help you create an increasing number sequence:
user> (let [f (comp (partial into {})
(partial map-indexed #(vector (inc %) (:account %2))))]
(f [{:account 123, :type "PK", :end "01.01.2013"} {:account 456 :type "GK" :end "01.07.2016"}]))
{1 123, 2 456}

Related

How to filter a map comparing it with another collection

I have a map with collection of these {:id 2489 ,values :.......} {:id 5647 ,values : .....} and so on till 10000 and I want to filter its value dependent on another collection which has ids of first one like (2489 ,......)
I am new to clojure and I have tried :
(into {} (filter #(some (fn [u] (= u (:id %))) [2489 3456 4567 5689]) record-sets))
But it gives me only the last that is 5689 id as output {:id 5689 ,:values ....}, while I want all of them, can you suggest what I can do.
One problem is that you start out with a sequence of N maps, then you try to stuff them into a single map. This will cause the last one to overwrite the first one.
Instead, you need to have the output be a sequence of M maps (M <= N).
Something like this is what you want:
(def data
[{:id 1 :stuff :fred}
{:id 2 :stuff :barney}
{:id 3 :stuff :wilma}
{:id 4 :stuff :betty}])
(let [ids-wanted #{1 3}
data-wanted (filterv
(fn [item]
(contains? ids-wanted (:id item)))
data)]
(println data-wanted))
with result:
[{:id 1, :stuff :fred}
{:id 3, :stuff :wilma}]
Be sure to use the Clojure CheatSheet: http://jafingerhut.github.io/cheatsheet/clojuredocs/cheatsheet-tiptip-cdocs-summary.html
I like filterv over plain filter since it always gives a non-lazy result (i.e. a Clojure vector).
You are squashing all your maps into one. First thing, for sake of performance, is to change your list of IDs into a set, then simply filter.
(let [ids (into #{} [2489 3456 4567 5689])]
(filter (comp ids :id) record-sets))
This will give you the sequence of correct maps. If you want to covert this sequence of maps into a map keyed by ID, you can do this:
(let [ids (into #{} [2489 3456 4567 5689])]
(->> record-sets
(filter (comp ids :id))
(into {} (map (juxt :id identity)))))
Another way to do this could be with the use of select-keys functions in Clojure
select-keys returns a map of only the keys given to the function
so given that your data is a list of maps we can convert it into a hash-map of ids using group-by and then call select-keys on it
(def data
[{:id 1 :stuff :fred}
{:id 2 :stuff :barney}
{:id 3 :stuff :wilma}
{:id 4 :stuff :betty}])
(select-keys (group-by :id data) [1 4])
; => {1 [{:id 1, :stuff :fred}], 4 [{:id 4, :stuff :betty}]}
However now the values is a map of ids. So in order to get the orignal structure back we need get all the values in the map and then flatten the vectors
; get all the values in the map
(vals (select-keys (group-by :id data) [1 4]))
; => ([{:id 1, :stuff :fred}] [{:id 4, :stuff :betty}])
; flatten the nested vectors
(flatten (vals (select-keys (group-by :id data) [1 4])))
; => ({:id 1, :stuff :fred} {:id 4, :stuff :betty})
Extracting the values and flattening might seem a bit inefficient but i think its less complex then the nested loop that needs to be done in the filter based methods.
You can using the threading macro to compose all the steps together
(-> (group-by :id data)
(select-keys [1 4])
vals
flatten)
Another thing that you can do is to store the data as a map of ids from the beginning this way using select keys wont require group-by and the result wont require flattening.
Update all keys in a map
(update-values (group-by :id data) first)
; => {1 {:id 1, :stuff :fred}, 2 {:id 2, :stuff :barney}, 3 {:id 3, :stuff :wilma}, 4 {:id 4, :stuff :betty}}
This would probably be the most efficient for this problem but this structure might not work for every case.

Clojure apply list of functions to list of arguments

I have a list of functions which adjust price, and a list of products.
As an output, I expect to have the same list of products, but with adjusted prices.
Technically, there is a list of functions and a list of maps.
What I'm trying to achieve is to apply each function sequentially to each map in a list, while preserving initial structure
(def products
[{:id 1 :price 100}
{:id 2 :price 200}])
(defn change-price
"increase by 10 => (change-price product 10 +)"
[product amount price-func]
(update product :price #(int (price-func amount %))))
(defn adjust-price
[products]
;;fn-list is a list of price adjuster functions
(let [fn-list [#(change-price % 10 +)
#(change-price % 50 -)]]
;; so I map a function to each product
;; which applies all adjsuter functions to that product
(merge (map (fn [prod]
(map #(% prod) fn-list)) products)))
It seems I don't understand how to reduce the result properly, because what I'm getting is a nested list like
(change-price products)
=> (({:id 1, :price 110} {:id 1, :price -50})
({:id 2, :price 210} {:id 2, :price -150}))
But I expect
({:id 1, :price 60} {:id 2, :price 160})
Thank you in advance.
It seems that you want to apply a composition of your functions:
(defn adjust-price
[products]
(let [fn-list [#(change-price % 10 +)
#(change-price % 50 -)]
f (apply comp fn-list)]
(map f products)))
the thing is map doesn't 'squash' results : it just makes list[n] => list[n].
what you need is reduce, something like this:
user> (let [fn-list [#(change-price % 10 +)
#(change-price % 50 -)]]
(map (fn [p] (reduce #(%2 %1) p fn-list))
products))
;;=> ({:id 1, :price -60} {:id 2, :price -160})
also you would have to rewrite your change-price function, since it has the wrong number of args here: (price-func amount %) => (price-func % amount)
You aren't mutating the hashmap passed in so when you call two different functions on an item the way you are, you will get to separate results.
In the 'adjust price' function, since you are using 'map' to go through the 'change price' functions, you are currently saying, run the first change price function once, return a value, then run the second price function once, return a separate value resulting in:
({:id 1, :price 110} {:id 1, :price -50})
The above answer is good, just thought I'd add another way to do it using a threaded function so that you don't have to worry about order.
(defn adjust-price
[products]
(let [f #(-> %
(change-price 10 +)
(change-price 50 -))]
(map f products)))
remember, single thread '->' means that you are passing the result of the current line(function) down to the next line(function), and it will be used as the first parameter
(ps. I know this is an old post, but hopefully this help someone else in the future:)

How to transform object keys and values in Clojure

I'm using Datomic, although it doesn't particularly matter for this question. But it typically returns namespaced keys (and enum values are returned as namespaces keywords also). I want to translate the potentially nested structure to strip the namespaces from the keys and from values (and also string-ify the values of enums). I'm doing this because I'll return the result in a JSON REST API and the namespacing doesn't make much sense in that context. Here's a simple example structure:
{
:person/name "Kevin"
:person/age 99
:person/gender :gender/M
:person/address {
:address/state :state/NY
:address/city "New York"
:address/zip "99999"
}
}
And I'm hoping to translate to:
{
:name "Kevin"
:age 99
:gender "M"
:address {
:state "NY"
:city "New York"
:zip "99999"
}
}
One thing I know I can do is use (postwalk-replace {:person/name :name :person/age :age :person/gender :gender :person/address :address :address/city :city :address/state :state :address/zip :zip} the-entity) and that covers the keys, but not the values.
What other options do I have?
You can use clojure.walk/postwalk. Simple version doesn't differentiate between keywords as keys or values in a map, simply converts all keys to strings:
(def data {:person/name "Kevin"
:person/age 99
:person/gender :gender/M
:person/address {:address/state :state/NY
:address/city "New York"
:address/zip "99999"}})
(clojure.walk/postwalk
(fn [x]
(if (keyword? x)
(name x)
x))
data)
;; => => {"name" "Kevin", "age" 99, "gender" "M", "address" {"state" "NY", "city" "New York", "zip" "99999"}}
To implement exactly what you want you need to handle keys and values in a map separately:
(defn transform-keywords [m]
(into {}
(map (fn [[k v]]
(let [k (if (keyword? k) (keyword (name k)) k)
v (if (keyword? v) (name v) v)]
[k v]))
m)))
(clojure.walk/postwalk
(fn [x]
(if (map? x)
(transform-keywords x)
x))
data)
;; => => {:name "Kevin", :age 99, :gender "M", :address {:state "NY", :city "New York", :zip "99999"}}
As a side note: in my experience, the impedance mismatch between namespace-qualified and non-namespace-qualified keys at the boundary of your system can be an ongoing pain; what's more, having namespaced-qualified keys has significant advantages regarding code clarity (very good data traceability).
So I wouldn't give up namespace-qualified keys too readily. If EDN's syntax for namespacing (with dots and slashes) doesn't suit the consumers of your API, you may even want to use something more conventional like underscores (e.g :person_name instead of :person/name); a bit uglier, but it still gives you most of the benefits of namespace-qualified keys, you will not even need to transform the data structures, and Datomic won't mind.

Clojure - Working with list and date-time

I am quite stuck in this scenario.
I have a list of atoms representing bank transactions.
(#<Ref#29a71299: {:desc "DESC1", :amount 150, :date #<LocalDate 2017-01-10>}>)
(#<Ref#5a4ebf03: {:desc "DESC2", :amount 250, :date #<LocalDate 2017-01-10>}>)
(#<Ref#5a4ebf03: {:desc "DESC3", :amount -250, :date #<LocalDate 2017-01-11>}>)
(#<Ref#5a4ebf03: {:desc "DESC4", :amount 50, :date #<LocalDate 2017-01-12>}>)
I need calculate the balance account in the end of the day, so I should grab all transactions separated per day to know the balance in the end of the day.
Someone did it before ? What is the best way to filter dates and do this math ? I am still noob/student in clojure.
obs. I am using this library to work with date Jodatime
A great way to approach problems in Clojure is to think:
How can I break this problem down (this is usually the hard part)
How can I solve each problem alone
How do I compose these solutions (this is usually the easy part)
Applying this to your problem I see these problems:
segmenting a list of maps by a property of one of the keys
(partition-by ... something ...)
summing all the values of one of the keys in each of a sequence of maps
(map (reduce ...))
making an output format with the data and the sum from each segment
(map ... something)
And the composing part is likely just nesting each of these as nested function calls. Nested function calls can be written using the thread-last maco and will look something like this:
(->> data
(... problem one solution ...)
(problem two solution)
(some output formatting for problem three))
You may want to break it down this way:
(defn per-day-balance [txns]
(->> txns
(partition-by :date)
(map (fn [[{date :date} :as txns]]
{:date date :balance (reduce + (map :amt txns))}))))
Find the daily balance assuming everyday starts with 0. Sample run:
(def txns [{:date 1 :amt 10}
{:date 1 :amt 3}
{:date 2 :amt 9}
{:date 2 :amt -11}
{:date 3 :amt 13}])
user> (per-day-balance txns)
=> ({:date 1, :balance 13} {:date 2, :balance -2} {:date 3, :balance 13})
Now add a reduction function to get the running total. The reduction function simply 'update' the cumulative balance:
(defn running-balance [bals]
(let [[day-1 & others] bals]
(reductions
(fn [{running :balance} e] (update e :balance + running))
day-1
others)))
Sample run:
user> (->> txns
per-day-balance
running-balance)
=> ({:date 1, :balance 13} {:date 2, :balance 11} {:date 3, :balance 24})
Note: You can use whatever data type for :date field. Secondly, I deliberately avoid atom to make the functions pure.
This ended up getting more complicated than I thought it would. I looked at partition-by, and you should almost definitely use that instead. It's perfectly suited for this problem. This is just an example of how it could be done with a loop:
(defn split-dates [rows]
(loop [[row & rest-rows] rows ; Split on the head
last-date nil
acc [[]]]
(if row
(let [current-date (last row)]
(recur rest-rows current-date
; If the day is the same as the previous row
(if (or (nil? last-date) (= current-date last-date))
; Update the current day list with the new row
(update acc (dec (count acc))
#(conj % row))
; Else, create a new list of rows for the new date
(conj acc [row]))))
acc)))
(clojure.pprint/pprint
(split-dates
[[0 1 "16.12.25"]
[2 3 "16.12.25"]
[4 5 "16.12.26"]
[6 7 "16.12.26"]
[8 9 "16.12.26"]
[1 9 "16.12.27"]]))
[[[0 1 "16.12.25"] [2 3 "16.12.25"]]
[[4 5 "16.12.26"] [6 7 "16.12.26"] [8 9 "16.12.26"]]
[[1 9 "16.12.27"]]]
Notes:
This assumes the dates are in the last column, and that the rows are sorted by date.
It returns [[]] when given given an empty list. This may or may not be what you want.

Deep data structure match & replace first

I'm trying to figure out an idiomatic, performant, and/or highly functional way to do the following:
I have a sequence of maps that looks like this:
({:_id "abc" :related ({:id "123"} {:id "234"})}
{:_id "bcd" :related ({:id "345"} {:id "456"})}
{:_id "cde" :related ({:id "234"} {:id "345"})})
The :id fields can be assumed to be unique within any one :_id.
In addition, I have two sets:
ids like ("234" "345") and
substitutes like ({:id "111"} {:id "222"})
Note that the fact that substitutes only has :id in this example doesn't mean it can be reduced to a collection of ids. This is a simplified version of a problem and the real data has other key/value pairs in the map that have to come along.
I need to return a new sequence that is the same as the original but with the values from substitutes replacing the first occurrence of the matching id from ids in the :related collections of all of the items. So what the final collection should look like is:
({:_id "abc" :related ({:id "123"} {:id "111"})}
{:_id "bcd" :related ({:id "222"} {:id "456"})}
{:_id "cde" :related ({:id "234"} {:id "345"})})
I'm sure I could eventually code up something that involves nesting maps and conditionals (thinking in iterative terms about loops of loops) but that feels to me like I'm not thinking functionally or cleverly enough given the tools I might have available, either in clojure.core or extensions like match or walk (if those are even the right libraries to be looking at).
Also, it feels like it would be much easier without the requirement to limit it to a particular strategy (namely, subbing on the first match only, ignoring others), but that's a requirement. And ideally, a solution would be adaptable to a different strategy down the line (e.g. a single, but randomly positioned match). The one invariant to strategy is that each id/sub pair should used only once. So:
Replace one, and one only, occurrence of a :related value whose :id matches a value from ids with the corresponding value from substitutes, where the one occurrence is the first (or nth or rand-nth...) occurrence.
(def id-mapping (zipmap ids
(map :id substitutes)))
;; id-mapping -> {"345" "222", "234" "111"}
(clojure.walk/prewalk-replace id-mapping original)
Assuming that the collection is called results:
(require '[clojure.zip :as z])
(defn modify-related
[results id sub]
(loop [loc (z/down (z/seq-zip results))
done? false]
(if (= done? true)
(z/root loc)
(let [change? (->> loc z/node :_id (= id))]
(recur (z/next (cond change?
(z/edit loc (fn [_] identity sub))
:else loc))
change?)))))
(defn modify-results
[results id sub]
(loop [loc (z/down (z/seq-zip results))
done? false]
(if (= done? true)
(z/root loc)
(let [related (->> loc z/node :related)
change? (->> related (map :_id) set (#(contains? % id)))]
(recur (z/next (cond change?
(z/edit loc #(assoc % :related (modify-related related id sub)))
:else loc))
change?)))))
(defn sub-for-first
[results ids substitutes]
(let [subs (zipmap ids substitutes)]
(reduce-kv modify-results results subs)))