3

I have the following class:

public class Question
{
    public int    QuestionId { get; set; }
    public string Title { get; set; }
}

and in a variable I have:

IEnumerable<Question> _questions = GetQuestions.Get();

How can I take the collection of questions that are in the variable _questions and return at random a list of between 1 and 4 random questions out of the collection?

  • 2
    See http://stackoverflow.com/questions/1287567 and then just use `.Take()` with the number of questions you want (just use `Random.Next` to get the random number between 1 and 4). – Jon Skeet Jun 22 '13 at 08:43

2 Answers2

6

You can shuffle the list first and then use Take()

var result = _questions.OrderBy(x => Guid.NewGuid())
                       .Take(new Random().Next(4) + 1);
Matthew Strawbridge
  • 19,940
  • 10
  • 72
  • 93
cuongle
  • 74,024
  • 28
  • 151
  • 206
2

You can use Reservoir Sampling to make a single pass through a sequence of unknown size to select a random set of N items:

public static List<T> RandomlyChooseItems<T>(IEnumerable<T> items, int n, Random rng)
{
    var result = new List<T>(n);
    int index = 0;

    foreach (var item in items)
    {
        if (index < n)
        {
            result.Add(item);
        }
        else
        {
            int r = rng.Next(0, index + 1);

            if (r < n)
                result[r] = item;
        }

        ++index;
    }

    return result;
}

You use it like this:

IEnumerable<Question> questions = ...;
Random rng = new Random();
int questionCount = rng.Next(1, 5); // Get between 1 and 4 questions.

List<Question> randomQuestions = RandomlyChooseItems(questions, questionCount, rng);

This isn't usually much more efficient than copying the items into an array and doing a Fisher-Yates shuffle, but can be useful if the size of the list to choose from is very large and the number of items to choose is relatively small, since you only need to store the chosen items and not the original list of items.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276