3

I have a dictionary with restaurants and a type of restaurant like this: Dictionary<Restaurant, string> Where restaurants is my class and for simplicity I use a string for my type of restaurant.

Let's say I have 25 entries in my list, where there can be groups of 3, 4 or 5 restaurants with the same type. Now I want to narrow this down to select 1 random element per type.

I have found this page which explains how to pick random values from a dictionary, but how would I go around picking a random entry per grouping of type?

Let me know if I should elaborate more or if you need additional information!

Edit: example of my code:

Dictionary<Restaurant, string> restaurants = new Dictionary<Restaurant, string>();

randomRestaurants.Add(restaurants.GroupBy(x => x.string).//Randomly pick an entry per grouping and add it to list//

List<Restaurant> randomRestaurants = new List<Restaurant>();

For example: Possible types can be Italian, Fast food and Sushi

I would like to apologize for asking another confusing question. I'll pay more attention before I post something.

Community
  • 1
  • 1
  • 1
    Please provide us with a [mcve] that shows what your current code looks like. – dymanoid Nov 16 '16 at 13:48
  • I think it will be easier if you post your code – Yanga Nov 16 '16 at 13:50
  • 3
    Your dictionary is backwards. It should be from a restaurant type to a list of `Restaurants`. –  Nov 16 '16 at 13:50
  • Edited my post! –  Nov 16 '16 at 13:53
  • @Amy this is the way I get my data, how would I accomplish what you're saying with my dictionary? –  Nov 16 '16 at 13:55
  • Surely it would make more sense to have `Dictionary>`, so that your string refers to the category and you can pick random entries from the List associated with that. – Rudi Visser Nov 16 '16 at 13:55
  • @RudiVisser please read my question for amy, how would I do this based on my dictionary? Sadly, I can't change the way I get this data –  Nov 16 '16 at 13:56
  • 1
    To do it based on your dictionary, first transform it into a `Dictionary>` so you can work with it. –  Nov 16 '16 at 13:58
  • @Bas what is your data source exactly and why can't you remap it to something that makes sense? It sounds as though you get a list of Restaurants from somewhere and are then creating the dictionary yourself? Or are you getting this weirdly structured Dictionary in the first instance? – Rudi Visser Nov 16 '16 at 13:59
  • @RudiVisser the latter, I'm using a function of someone else who structured the dictionary this way. I can't alter this function to suit my needs since multiple other functions use it –  Nov 16 '16 at 14:00
  • 1
    You can make it make a bit more sense by doing this: `var sensibleDict = sillyDict.GroupBy(kv => kv.Value).ToDictionary(m => m.Key, m => m.Select(kv => kv.Key).ToList());` Then you can simply randomise the List (using a method such as ordering by Guid). I'll write a quick answer (note that it may not be the most efficient way) – Rudi Visser Nov 16 '16 at 14:02
  • Thank you rudi and amy! I haven't even thought of mapping it that way, and you're right it doesn't make sense. Once you post an answer, I'll accept it! –  Nov 16 '16 at 14:07

3 Answers3

1

First idea (for any usage of this list) would be to order your data source into a more sensible format, that is, a key value structure wherein the key type is the thing that identifies a subset of restaurants (string), and the value is a list of the Restaurants within it.

You can do this using code similar to the following:

var sensibleDict = sillyDict
    .GroupBy(kv => kv.Value)
    .ToDictionary(
        m => m.Key,
        m => m.Select(kv => kv.Key).ToList());

Now, we can simply randomise the result and take the first after randomisation to convert that into a Key => Single Value item.

var randomDict = sillyDict
    .GroupBy(kv => kv.Value)
    .ToDictionary(
        m => m.Key,
        m => m.Select(kv => kv.Key)
            .OrderBy(k => Guid.NewGuid())
            .Take(1)
            .First());

This will get you a Dictionary<string, Restaurant> wherein the first string is the type of food (ie. Italian, Sushi) and the second is the random Restaurant.

Note: As I said in my comment, this isn't the most efficient way to order a list if your dataset gets large. For better methods of shuffling a list, take a look at this answer - Randomise a List.

Community
  • 1
  • 1
Rudi Visser
  • 21,350
  • 5
  • 71
  • 97
0

try this way:

var restaurants = new Dictionary<string, List<Restaurant>();

//when u want to add a restaurant do this way:
var restaurant = new Restaurant { Type = "Sushi" };
List<Restaurant> res;
if(restaurants.TryGet(restaurant.Type, out res){
 res.Add(restaurant);
}
else restaurants.Add(restaurant.Type, new List<Restaurant> { restaurant });

// get random from a type
List<Restaurant> res2;
var type = "Sushi";
if(restaurants.TryGet(type, out res2){
 return res2[rand.Next(res2.Count)]
}
David Soler
  • 192
  • 1
  • 10
0

Based on the link you provided, you can use an extension method that takes IGrouping instead of a Dictionary (btw. why use a Dictionary in this way at all, wouldn't it be simpler to just add a property RestaurantType to the Restaurant class?)

public static class Ext
{
    public static IEnumerable<TValue> RandomValues<TKey, TValue>(this IGrouping<TKey, TValue> grouping, int count)
    {
        Random rand = new Random();
        List<TValue> values = grouping.ToList();
        int size;
        while (count>0 && values.Count>0)
        {
            size = values.Count;
            var v = values[rand.Next(size)];
            values.Remove(v);
            count--;
            yield return v;
        }
    }
}

using the code:

       // example data
       Dictionary<Restaurant, string> restaurants = new Dictionary<Restaurant, string>();
        restaurants.Add(new Restaurant("r1") ,"A");
        restaurants.Add(new Restaurant("r2") ,"B");
        restaurants.Add(new Restaurant("r3") ,"A");
        restaurants.Add(new Restaurant("r4") ,"B");
        restaurants.Add(new Restaurant("r5") ,"A");
        restaurants.Add(new Restaurant("r6") ,"B");
        restaurants.Add(new Restaurant("r7") ,"A");
        restaurants.Add(new Restaurant("r8") ,"B");

        // 3 Random restaurants from group "A"
        List<Restaurant> randomRestaurants = restaurants.GroupBy(a=>a.Value).FirstOrDefault(a=>a.Key=="A")
            .RandomValues(3).Select(x=>x.Key).ToList();
        randomRestaurants.Clear();

        // 1 random restaurant per group
         List<Restaurant> randomRestaurants2 = restaurants
             .GroupBy(a => a.Value)
             .Select(a => a.RandomValues(1).FirstOrDefault().Key)
             .ToList();

         // or, with SelectMany, 2 random restaurants per type:
              List<Restaurant> randomRestaurants3 = restaurants.GroupBy(a => a.Value)
             .SelectMany(a => a.RandomValues(2))
             .Select(d=>d.Key).ToList();
Arie
  • 5,251
  • 2
  • 33
  • 54
  • Rudi has helped me enough, but to elaborate on your first question, a restaurant can have multiple types. This was besides the question since I only wanted a random restaurant per type. This is why I only used a string in my example –  Nov 16 '16 at 14:21