291

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?

Abdullah Ilgaz
  • 719
  • 1
  • 17
  • 39
jay_t55
  • 11,362
  • 28
  • 103
  • 174

12 Answers12

482
  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]);
    
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
161

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>();
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • what is the complexity of those? does the lazy nature of IEnumerable mean that it isnt O(N)? – Dave Hillier Jun 19 '12 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)];` – MushroomSoda Nov 11 '12 at 03: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 Nov 13 '12 at 08:38
  • .OrderBy(.) does not create another list - It creates an object of type IEnumerable which is iterating through the original list in an ordered way. – Johan Tidén Aug 07 '13 at 12:24
  • 9
    The GUID generation algorithm is unpredictable but not random. Consider holding an instance of `Random` in static state instead. – Dai Aug 26 '16 at 23:16
105

You can do:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()
Felipe Pessoto
  • 6,855
  • 10
  • 42
  • 73
  • Beautiful. IN ASP.NET MVC 4.5, uisng a list, I had to change this to: list.OrderBy(x => Guid.NewGuid()).FirstOrDefault(); – Andy Brown Sep 03 '14 at 09:56
  • 3
    It won't matter in most cases but this is probably much slower than using rnd.Next. OTOH it will work on IEnumerable, not just lists. – solublefish Mar 22 '15 at 23:24
  • 24
    Not sure how random is that. Guids are unique, not random. – pomber Mar 07 '16 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](https://stackoverflow.com/a/48799167/197591) (plus [my comment](https://stackoverflow.com/questions/48799104/how-to-get-a-random-item-from-a-list#comment98458350_48799167)) to a similar question. – Neo Apr 29 '19 at 10:17
  • Guid.NewGuid() this is generate random uniq key for every item, then order by this uniq key. so, it's mean random ordered list. – Ramazan Sağır Aug 19 '20 at 07:04
  • fixed some issues at 2015 about that. https://github.com/dotnet/efcore/issues/2069 – Ramazan Sağır Aug 19 '20 at 07:24
  • 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? – Martin Meeser Sep 06 '22 at 20:07
33

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.

Shiva
  • 20,575
  • 14
  • 82
  • 112
Dave_cz
  • 1,180
  • 10
  • 17
  • 4
    Arrays in .NET already implement `IList` so the second overload is unnecessary. – Dai Aug 26 '16 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.

Joey
  • 344,408
  • 85
  • 689
  • 683
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.

Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91
5

Why not:

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}
Lucas
  • 1,259
  • 15
  • 25
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());
  • 4
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) 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 Jan 08 '15 at 01: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*". – David Ferenczy Rogožan Nov 13 '15 at 14:15
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;
}
Carlos Toledo
  • 2,519
  • 23
  • 23
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); 
bafsar
  • 1,080
  • 2
  • 16
  • 16
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)];
}