304

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

495
  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]);
    
Sign up to request clarification or add additional context in comments.

6 Comments

@McAdam331 Look up Fisher-Yates Shuffle algorithm: en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
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.ClayShannon No. The upperbound in the Next(max) call is exclusive.
What about when list is empty?
0, to 0 will be 0, because lower bound inclusive "wins" over upper bound exclusive. You will get a indexoutofbound exception. @tsu1980
|
167

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 Comments

what is the complexity of those? does the lazy nature of IEnumerable mean that it isnt O(N)?
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)];
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..
.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.
The GUID generation algorithm is unpredictable but not random. Consider holding an instance of Random in static state instead.
105

You can do:

list.OrderBy(x => Guid.NewGuid()).FirstOrDefault()

7 Comments

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.
Not sure how random is that. Guids are unique, not random.
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.
Guid.NewGuid() this is generate random uniq key for every item, then order by this uniq key. so, it's mean random ordered list.
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?
|
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 Comment

Arrays in .NET already implement IList so the second overload is unnecessary.
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.

Comments

7

.NET8 improved this significantly:

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

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

1 Comment

It bothers me that they did not make this support IEnumerable<T>. It was right there! Having an overload for that would be convenient.
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.

Comments

5

Why not:

public static T GetRandom<T>(this IEnumerable<T> list)
{
   return list.ElementAt(new Random(DateTime.Now.Millisecond).Next(list.Count()));
}

1 Comment

this IList<T> list is better. Otherwise multiple enumeration is possible.
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 Comments

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.
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".
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 Comment

You had forgotten to add Random class instance
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); 

Comments

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);

Comments

-3

Why not[2]:

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.