Alright, I wanted to take a stab at this without looking at someone else's code. Here's what I came up with.
Btw, I hope it was the Java tag that was in error and not the C# tag :D.
Here's the entire program. What follows below is an explanation of each piece
.NET Fiddle
I chose to take each element to be a portion in a ratio. Therefore, in your example, your total is 100 (20 + 80) meaning that the 20 content model should get chosen 20% of the time. If you want to constrain your content models such that their total weights add up to 100, that should be done at the time that you create them.
So here's my solution.
First the content models:
class ContentModel
{
public string Content { get; set; }
public int Weight { get; set; }
}
And then a list of test cases:
static List<ContentModel> contentOptions = new List<ContentModel>
{
new ContentModel
{
Content = "hello",
Weight = 20
},
new ContentModel
{
Content = "hey",
Weight = 80
},
new ContentModel
{
Content = "yo dawg",
Weight = 90
}
};
Given these test cases we would expect to see 'Hello' appear about 10.5% of the time (20 / (80 + 90 + 20)) * 100. And so on for the rest of the test cases.
Here's the generator that makes that happen:
Here all we're going to do is figure out what the total weight is that we're working with. Then we're going to pick a random number and go through each model asking "Is this number from this content model?" If no, then subtract that content model's weight and move to the next one until we get to a model where the selection - weight is < 0. In this case we have the model that was chosen. I hope that makes sense.
(Note: I chose to recalculate the total weight every time in case you change the source list of options. If you make that list readonly, then you could move that .Sum() call outside the while loop.)
static IEnumerable<string> GetGreetings()
{
Random generator = new Random();
while (true)
{
int totalWeight = contentOptions.Sum(x => x.Weight);
int selection = generator.Next(0, totalWeight);
foreach (ContentModel model in contentOptions)
{
if (selection - model.Weight > 0)
selection -= model.Weight;
else
{
yield return model.Content;
break;
}
}
}
}
And finally, here's the main method which will also test this whole thing:
static void Main(string[] args)
{
List<string> selectedGreetings = new List<string>();
/* This will get 1000 greetings,
* which are the Content property of the models, group them by the greeting,
* count them, and then print the count along with the greeting to the Console.
*/
GetGreetings()
.Take(1000)
.GroupBy(x => x)
.Select(x => new { Count = x.Count(), Content = x.Key })
.ToList()
.ForEach(x => Console.WriteLine("{0} : {1}", x.Content, x.Count));
Console.ReadLine();
}
Here are my results from a run through:
