简体   繁体   中英

How do I select a random element from an ets set in Erlang/Elixir?

I have a large number of processes that I need to keep track of in an ets set, and then randomly select single processes. So I created the set like this:

:ets.new(:pid_lookup, [:set, :protected, :named_table])

then for argument's sake let's just stick self() in it 1000 times:

Enum.map 1..1000, fn x -> :ets.insert(:pid_lookup, {x, self()}) end

Now I need to select one at random. I know I could just select a random one using :ets.lookup(:pid_lookup, :rand.uniform(1000)) , but what if I don't know the size of the set (in the above case, 1000) in advance?

How do I find out the size of an ets set? And/or is there a better way to choose a random pid from an ets data structure?

  • If keys are sequential number
tab = :ets.new(:tab, [])
Enum.each(1..1000, & :ets.insert(tab, {&1, :value}))
size = :ets.info(tab,  :size) 
# size = 1000
value_picked_randomly = :ets.lookup(tab, Enum.random(1..1000)) 

:ets.info(tab, :size) returns a size of a table; which is a number of records inserted on given table.

  • If you don't know that the keys are
first = :ets.first(tab)
:ets.lookup(tab, first)
func = fn key->
    if function_that_may_return_true() do
        key = case :ets.next(tab, key) do
         :'$end_of_table' -> throw :reached_end_of_table
         key -> func.(key)
        end
    else 
        :ets.lookup(tab, key)
    end
end
func.()

func will iterate over the ets table and returns a random value. This will be time consuming, so it will not be an ideal solution for tables with large number of records.

As I understood from the comments, this is an XY Problem .

What you essentially need is to track down the changing list and pick up one of its elements randomly. ETS in general and :ets.set in particular are by no mean intended to be queried for size. They serve different purposes.

Spawn an Agent within your supervision tree, holding the list of PIDs of already started servers and use Kernel.length/1 to query its size, or even use Enum.random/1 if the list is not really huge (the latter traverses the whole enumerable to get a random element.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM