user1213488
user1213488

Reputation: 503

Get random element from HashSet<T>?

I'm using the following piece of code to load my text file into a HashSet<string>.

HashSet<string> hashs = new HashSet<string>(File.ReadLines("textFile.txt"));

Am wondering if there is any easy way to get a random line from it?

Lets assume the textFile.txt contains 10 lines, I would like to randomize and grab one of those existing lines.

Upvotes: 14

Views: 30950

Answers (6)

Emil Forslund
Emil Forslund

Reputation: 547

If you are planning on drawing multiple random values, the efficient way would be to use a Dictionary with integer keys to store the information.

HashSet<string> hashs = new HashSet<string>();
Dictionary<int, string> lookup = new Dictionary<int, string>();
foreach (string line in File.ReadLines("textFile.txt")) {
    if (hashs.Add(line)) {
        lookup.Add(lookup.Count, line);
    }
}
        
int randomInt = new Random().Next(lookup.Count);
string randomLine = lookup[randomInt];

(In this example, you could use a List instead, but with a dictionary you can also remove individual elements without affecting the order).

Upvotes: 0

bbenno
bbenno

Reputation: 384

Since .Net Framework 3.5 you can use Linq with its Enumerable.First() extension method. Without specifying any condition as parameter this method will return

the first element of a sequence.

You should consider that using Enumerable.First() requires your HashSet<> to contain at least one element. To check this precondition you could either use HashSet<>.Count or via Linq with Enumerable.Any() again without specifying a condidion.

HashSet<T> hashSet = new HashSet<T>();

...

if(hashSet.Any())
{
  T randomElement = hashSet.First()
}

As an alternative to the approach above you could use Enumerable.FirstOrDefault() if you would like to retrieve any default value in case the HashSet<T> is empty.

T randomElement = hashSet.FirstOrDefault(default(T));

Upvotes: -2

katbyte
katbyte

Reputation: 2785

a simple answer like the accepted one is possible without enumerating the entire array every time:

private static readonly Random     random  = new Random();
private static readonly HashSet<T> hashset = new HashSet<T>();

...

T element = hashset.ElementAt(random.Next(hashset.Count));

Upvotes: 40

Vasea
Vasea

Reputation: 5333

Or maybe a more general solution for any enumerable

public static class RandomExtensions
{
    private static readonly Random rnd = new Random();
    private static readonly object sync = new object();

    public static T RandomElement<T>(this IEnumerable<T> enumerable) {
        if (enumerable == null)
            throw new ArgumentNullException("enumerable");

        var count = enumerable.Count();

        var ndx = 0;
        lock (sync) 
            ndx = rnd.Next(count); // returns non-negative number less than max

        return enumerable.ElementAt(ndx); 
    }
}

Upvotes: 1

Greg Bahm
Greg Bahm

Reputation: 648

Random randomizer = new Random();
string[] asArray = hashs.ToArray()
string randomLine = asArray[randomizer.Next(asArray.length)];

Upvotes: 17

Attila
Attila

Reputation: 28782

You can generate a random number between 0 and the size of the set, then iterate through the set up until you reach the item whose index is the same as the generated number. Then select this item as the random element

Upvotes: 3

Related Questions