296

I have an ArrayList, and I need to be able to click a button and then randomly pick out a string from that list and display it in a messagebox.

How would I go about doing this?

13 Answers 13

491
  1. Create an instance of Random class somewhere. Note that it's pretty important not to create a new instance each time you need a random number. You should reuse the old instance to achieve uniformity in the generated numbers. You can have a static field somewhere (be careful about thread safety issues):

    static Random rnd = new Random();
    
  2. Ask the Random instance to give you a random number with the maximum of the number of items in the ArrayList:

    int r = rnd.Next(list.Count);
    
  3. Display the string:

    MessageBox.Show((string)list[r]);
    
6
  • 7
    @McAdam331 Look up Fisher-Yates Shuffle algorithm: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle Commented Sep 21, 2014 at 6:04
  • 3
    Should this be "rnd.Next(list.Count-1)" instead of "rnd.Next(list.Count)" to avoid accessing element max, which would be one beyond the presumably 0-based index? Commented Feb 12, 2016 at 2:02
  • 33
    @B.ClayShannon No. The upperbound in the Next(max) call is exclusive. Commented Feb 12, 2016 at 3:20
  • 1
    What about when list is empty?
    – tsu1980
    Commented Dec 7, 2019 at 0:38
  • 1
    0, to 0 will be 0, because lower bound inclusive "wins" over upper bound exclusive. You will get a indexoutofbound exception. @tsu1980
    – Default
    Commented Jul 25, 2020 at 7:20
164

I usually use this little collection of extension methods:

public static class EnumerableExtension
{
    public static T PickRandom<T>(this IEnumerable<T> source)
    {
        return source.PickRandom(1).Single();
    }

    public static IEnumerable<T> PickRandom<T>(this IEnumerable<T> source, int count)
    {
        return source.Shuffle().Take(count);
    }

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        return source.OrderBy(x => Guid.NewGuid());
    }
}

For a strongly typed list, this would allow you to write:

var strings = new List<string>();
var randomString = strings.PickRandom();

If all you have is an ArrayList, you can cast it:

var strings = myArrayList.Cast<string>();
5
  • what is the complexity of those? does the lazy nature of IEnumerable mean that it isnt O(N)? Commented Jun 19, 2012 at 21:51
  • 25
    This answer re-shuffles the list every time you pick a random number. It'd be much more efficient to return a random index value, especially for large lists. Use this in PickRandom - return list[rnd.Next(list.Count)]; Commented Nov 11, 2012 at 3:24
  • This doesnt shuffle the original list, it does on another list in fact which still may not be good for efficiency if list is large enough..
    – nawfal
    Commented Nov 13, 2012 at 8:38
  • .OrderBy(.) does not create another list - It creates an object of type IEnumerable<T> which is iterating through the original list in an ordered way. Commented Aug 7, 2013 at 12:24
  • 9
    The GUID generation algorithm is unpredictable but not random. Consider holding an instance of Random in static state instead.
    – Dai
    Commented Aug 26, 2016 at 23:16
105

You can do:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()
7
  • 3
    It won't matter in most cases but this is probably much slower than using rnd.Next. OTOH it will work on IEnumerable<T>, not just lists. Commented Mar 22, 2015 at 23:24
  • 24
    Not sure how random is that. Guids are unique, not random.
    – pomber
    Commented Mar 7, 2016 at 18:28
  • 1
    I think a better and extended version of this answer and @solublefish's comment is nicely summed up in this answer (plus my comment) to a similar question.
    – Neo
    Commented Apr 29, 2019 at 10:17
  • 1
    Guid.NewGuid() this is generate random uniq key for every item, then order by this uniq key. so, it's mean random ordered list. Commented Aug 19, 2020 at 7:04
  • 1
    this is highly compute intense for no reason, I do not see any reason to use it, prefer random.next, not to mention that the random function distribution is not clear, do you want a coin flip when you have 2 elements? Commented Sep 6, 2022 at 20:07
34

Or simple extension class like this:

public static class CollectionExtension
{
    private static Random rng = new Random();

    public static T RandomElement<T>(this IList<T> list)
    {
        return list[rng.Next(list.Count)];
    }

    public static T RandomElement<T>(this T[] array)
    {
        return array[rng.Next(array.Length)];
    }
}

Then just call:

myList.RandomElement();

Works for arrays as well.

I would avoid calling OrderBy() as it can be expensive for larger collections. Use indexed collections like List<T> or arrays for this purpose.

1
  • 5
    Arrays in .NET already implement IList so the second overload is unnecessary.
    – Dai
    Commented Aug 26, 2016 at 23:19
28

Create a Random instance:

Random rnd = new Random();

Fetch a random string:

string s = arraylist[rnd.Next(arraylist.Count)];

Remember though, that if you do this frequently you should re-use the Random object. Put it as a static field in the class so it's initialized only once and then access it.

6

I'll suggest different approach, If the order of the items inside the list is not important at extraction (and each item should be selected only once), then instead of a List you can use a ConcurrentBag which is a thread-safe, unordered collection of objects:

var bag = new ConcurrentBag<string>();
bag.Add("Foo");
bag.Add("Boo");
bag.Add("Zoo");

The EventHandler:

string result;
if (bag.TryTake(out result))
{
    MessageBox.Show(result);
}

The TryTake will attempt to extract an "random" object from the unordered collection.

5

Why not:

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}
1
  • this IList<T> list is better. Otherwise multiple enumeration is possible. Commented Jul 15, 2020 at 7:55
3
ArrayList ar = new ArrayList();
        ar.Add(1);
        ar.Add(5);
        ar.Add(25);
        ar.Add(37);
        ar.Add(6);
        ar.Add(11);
        ar.Add(35);
        Random r = new Random();
        int index = r.Next(0,ar.Count-1);
        MessageBox.Show(ar[index].ToString());
2
  • 4
    While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.
    – gunr2171
    Commented Jan 8, 2015 at 1:19
  • 3
    I would say, that the maxValue parameter of method Next should be just a number of elements in a list, not minus one, because according to a documentation "maxValue is the exclusive upper bound of the random number". Commented Nov 13, 2015 at 14:15
3

.NET8 improved this significantly:

string[] stringArray = ["first","second","third"];

string rndItem = Random.Shared.GetItems(stringArray, 1)[0];
2

I have been using this ExtensionMethod for a while:

public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int count)
{
    if (count <= 0)
      yield break;
    var r = new Random();
    int limit = (count * 10);
    foreach (var item in list.OrderBy(x => r.Next(0, limit)).Take(count))
      yield return item;
}
1
  • You had forgotten to add Random class instance
    – bafsar
    Commented Mar 9, 2017 at 14:04
1

I needed to more item instead of just one. So, I wrote this:

public static TList GetSelectedRandom<TList>(this TList list, int count)
       where TList : IList, new()
{
    var r = new Random();
    var rList = new TList();
    while (count > 0 && list.Count > 0)
    {
        var n = r.Next(0, list.Count);
        var e = list[n];
        rList.Add(e);
        list.RemoveAt(n);
        count--;
    }

    return rList;
}

With this, you can get elements how many you want as randomly like this:

var _allItems = new List<TModel>()
{
    // ...
    // ...
    // ...
}

var randomItemList = _allItems.GetSelectedRandom(10); 
1

Printing randomly country name from JSON file.
Model:

public class Country
    {
        public string Name { get; set; }
        public string Code { get; set; }
    }

Implementaton:

string filePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..\..\")) + @"Data\Country.json";
            string _countryJson = File.ReadAllText(filePath);
            var _country = JsonConvert.DeserializeObject<List<Country>>(_countryJson);


            int index = random.Next(_country.Count);
            Console.WriteLine(_country[index].Name);
-3

Why not[2]:

public static T GetRandom<T>(this List<T> list)
{
     return list[(int)(DateTime.Now.Ticks%list.Count)];
}
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.