1

I am using LINQ Bridge targetting the .NET 2.0 Framework, and I am getting the following error. Just wanting the first element from a collection, chosen at random. Not concerned about performance in this particular case.

var result = someCollection.OrderBy(g => Guid.NewGuid()).Take(1).FirstOrDefault();

someCollection is a List<string>. The values in the collection are unique.

Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. x: '', x's type: 'Tuple2', IComparer: 'System.Array+FunctorComparer1[LinqBridge.Tuple`2[System.String,System.Int32]]'.

But it seems to work just fine in .NET 4.0. Is there a workaround for this? Unfortunately I'm stuck using .NET 2.0 for this scenario.

EDIT Using the latest version of LINQ Bridge (1.2)

Bryan Crosby
  • 6,486
  • 3
  • 36
  • 55
  • See also http://stackoverflow.com/questions/4129995/why-does-using-random-in-sort-causing-unable-to-sort-icomparer-compare-error ... they seem to have the same issue as you, but unfortunately they solve the issue with LINQ. – Scott Rippey Dec 02 '11 at 00:22

1 Answers1

2

Yet another update

I found this question that has the same issue as you: Why does using Random in Sort causing [Unable to sort IComparer.Compare error]
The problem is that LINQBridge uses List<>.Sort internally, which complains when using a "unstable" comparing algorithm, so you unfortunately can't randomize this way.

As an alternative, here's some great code to randomize or to choose a random item:

    private static Random rnd = new Random();
    /// <summary>
    /// Chooses one of the items at random.
    /// 
    /// Returns default if there are no items.
    /// </summary>
    public static T RandomOrDefault<T>(this IEnumerable<T> source)
    {
        // We need the count:
        var buffer = source as ICollection<T> ?? source.ToList(); // (iterate only once)
        var itemCount = buffer.Count;
        if (itemCount == 0)
        {
            return default(T);
        }

        var index = rnd.Next(itemCount);
        return buffer.ElementAt(index);
    }

    /// <summary>
    /// Randomizes the order of the elements of a sequence. 
    /// </summary>
    public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source)
    {
        // This code is an implementation of the Fisher–Yates shuffle.
        // The code was obtained from:
        // https://stackoverflow.com/questions/1287567/c-is-using-random-and-orderby-a-good-shuffle-algorithm/1665080#1665080
        T[] elements = source.ToArray();
        // Note i > 0 to avoid final pointless iteration
        for (int i = elements.Length - 1; i > 0; i--)
        {
            // Swap element "i" with a random earlier element it (or itself)
            int swapIndex = rnd.Next(i + 1);
            yield return elements[swapIndex];
            elements[swapIndex] = elements[i];
            // we don't actually perform the swap; we can forget about the
            // swapped element because we already returned it.
        }

        // there is one item remaining that was not returned - we return it now
        yield return elements[0];
    }

Update

This exception really looks like a LINQBridge bug. I would recommend updating to the latest version. There's no other apparent reason that you're seeing this issue.

Additional Info

You can use a Random instead of Guid like so:

var rnd = new Random();
var result = someCollection.OrderBy(g => rnd.Next()).Take(1).FirstOrDefault();

Also, .Take(1) is absolutely unnecessary when followed by .FirstOrDefault()

Community
  • 1
  • 1
Scott Rippey
  • 15,614
  • 5
  • 70
  • 85