How to get a random acces by index on a hash map in Clojure?
Asked Answered
N

2

7

I'd like to perform a number (MAX_OPERATIONS) of money transfers from one account to another. The accounts are stored as refs in a hash-map caller my-map (int account-id, double balance).

The money transfer takes a "random index" from the hash map and passes it as account-from to transfer. account-destination and amount should both be fixed.

Unfortunately I can't make it work.

(defn transfer [from-account to-account amount]
  (dosync
    (if (> amount @from-account)
      (throw (Exception.  "Not enough money")))
    (alter from-account - amount)
    (alter to-account + amount)))

(defn transfer-all []
  (dotimes [MAX_OPERATIONS]
    (transfer (get mymap (rand-int[MAX_ACCOUNT]) :account-id) account-destination amount)))
Nietzsche answered 5/3, 2012 at 14:16 Comment(0)
J
8

Maps do not implament nth so you need to use an intermediate structure that does implament nth. you can make a seq of either just the keys or the entire map entries depending on what you want as output. I like using rand-nth for this kind of thing because it reads nicely

you can get an nthable seq of the keys and then use one at random:

user> (def mymap {:a 1, :b 2, :c 3})
#'user/mymap
user> (get mymap (rand-nth (keys mymap)))
1
user> (get mymap (rand-nth (keys mymap)))
1
user> (get mymap (rand-nth (keys mymap)))
3

Or you can turn the map into an nthable vector and then grab one at random

user> (rand-nth (vec mymap))
[:a 1]
user> (rand-nth (vec mymap))
[:c 3]
Jasso answered 5/3, 2012 at 21:58 Comment(2)
... or ((rand-nth (keys mymap)) mymap ) to do it without get.Orpah
A bit cleaner: (some-> x keys rand-nth x)Beatification
A
6

A couple of issues I see immediately:

Your syntax for dotimes is wrong, you need to include a loop variable. Something like:

(dotimes [i MAX_OPERATIONS]
   ....)

Also rand-int just needs an integer parameter raher than a vector, something like:

(rand-int MAX_ACCOUNT)

Also, I'm not sure that your (get ...) call is doing quite what you intend. As currently written, it will return the keyword :account-id if it doesn't find the randomly generated integer key, which is going to cause problems as the transfer function requires two refs as the from-account and to-account.

As more general advice, you should probably try coding this up bit by bit at the REPL, checking that each part works as intended. This is often the best way to develop in Clojure - if you write too much code at once without testing it then it's likely to contain several errors and you may get lost trying to track down the root of the problem.

Abruption answered 5/3, 2012 at 14:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.