270

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
  • 636
  • 1
  • 17
  • 37
jay_t55
  • 10,752
  • 26
  • 96
  • 170

12 Answers12

456
  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]);
    
mmx
  • 402,675
  • 87
  • 836
  • 780
  • Is there any good way to modify this so that a number isn't repeated? Let's say I wanted to shuffle a deck of cards by randomly selecting one at a time, but obviously can't select the same card twice. – AdamMc331 Sep 21 '14 at 05:26
  • 7
    @McAdam331 Look up Fisher-Yates Shuffle algorithm: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle – mmx Sep 21 '14 at 06: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? – B. Clay Shannon-B. Crow Raven Feb 12 '16 at 02:02
  • 32
    @B.ClayShannon No. The upperbound in the `Next(max)` call is exclusive. – mmx Feb 12 '16 at 03:20
  • 1
    What about when list is empty? – tsu1980 Dec 07 '19 at 00: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 Jul 25 '20 at 07:20
156

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
  • 218,019
  • 46
  • 414
  • 706
  • 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
  • 22
    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)];` – swax 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
  • 7
    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
102

You can do:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()
Felipe Pessoto
  • 6,707
  • 9
  • 41
  • 72
  • 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
  • 22
    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
30

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
  • 19,630
  • 13
  • 80
  • 109
Dave_cz
  • 1,110
  • 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
  • 330,812
  • 81
  • 665
  • 668
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,199
  • 14
  • 25
5

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
  • 6,186
  • 7
  • 39
  • 70
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());
  • 3
    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,251
  • 20
  • 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
  • 972
  • 2
  • 14
  • 15
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)];
}