10

I have a HashMap<u32, Sender>. Sender is a open connection object and the key is a user id. Each user can connect from multiple devices. I need to store all possible open connections for the same user id. After this I can iterate and send messages to all open connections for same user.

The above HashMap only stores each user id and connection once. I need to get one key with multiple values. How can I make the value into a list or an array, so I can see which connections exist and send to them all?

I am not talking about different value types, like enums. I am talking about the same type values but more than one. Maybe HashMap is not designed for this?

Alternative ideas are also welcomed.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Dennis
  • 1,805
  • 3
  • 22
  • 41
  • 4
    That is not a map data structure. You are looking for a multi-value map, or a bag. Have you tried something like `HashMap>`? – E_net4 Jul 29 '18 at 21:33
  • @E_net4, I am bit unfamiliar with Vec. (Very beginner here). I saw them in few tutorial but didn't catch the idea yet. If this is what I mean, Could you please elaborate with sample so I can try on my end? Btw, I should be able to add/remove values too (after set) – Dennis Jul 29 '18 at 21:37
  • 3
    You may start with what's on [The Rust Programming Language (chapter 8)](https://doc.rust-lang.org/stable/book/second-edition/ch08-01-vectors.html#storing-lists-of-values-with-vectors), as it covers the usage of `Vec` to store lists of values pretty well. There is no multi-valued map collection type in the standard library, likely because it can be implemented by combining a map with a vector or set. – E_net4 Jul 29 '18 at 21:42
  • Working on it. Thanks! – Dennis Jul 29 '18 at 21:49

1 Answers1

27

To do this with a HashMap you should use a Vec as the values, so that each key can point to multiple Senders. The type then would be HashMap<u32, Vec<Sender>>.

Using this structure, just using insert() can get clunky when you need to mutate the values like this, but instead you can use the Entry API for retrieving and updating records in one go. For example:

let mut hash_map: HashMap<u32, Vec<Sender>> = HashMap::new();

hash_map.entry(3)
    // If there's no entry for key 3, create a new Vec and return a mutable ref to it
    .or_default()
    // and insert the item onto the Vec
    .push(sender); 

You could also use the multimap crate, which does something similar under the hood, but adds a layer of abstraction. You might find it easier to work with:

let mut multi_map = MultiMap::new();

multi_map.insert(3, sender_1);
multi_map.insert(3, sender_2);

The method multi_map.get(key) will the first value with that key, while multi_map.get_vec(key) will retrieve all of them.

Arnavion
  • 3,627
  • 1
  • 24
  • 31
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Thank you for both samples. May I ask how do you delete a specifc Vec (lets say i want to remove one of the vec's but not whole entry.) I cannot find proper remove for values – Dennis Jul 30 '18 at 15:01
  • @Pratha In both cases, the method is named the same. Just do `my_map.remove(key)`. – Peter Hall Jul 30 '18 at 15:07
  • But this removes key with all values. Am I right? I would like to keep key and values but remove one of its values. – Dennis Jul 30 '18 at 15:12
  • @Pratha Ok, you don't want to remove the Vec, but just an element of it? In the code above, `.entry(key).or_insert_with(Vec::new)` gives you a mutable reference to a `Vec`. If you chose to use `MultiMap` then `.get_vec_mut(key)` will give you the same thing. In both cases you'll have a mutable Vec reference so you can use any of the [Vec methods](https://doc.rust-lang.org/std/vec/struct.Vec.html#methods) to do what you need. If you're still having problems, I suggest you spend a bit of time familiarising yourself with `Vec`, and possibly ask a new question if you really get stuck. – Peter Hall Jul 30 '18 at 15:49
  • Exactly. This will work. You explained very well thank you again. I will definitely do some learning on Vec. – Dennis Jul 30 '18 at 15:50