Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can LINQ use binary search when the collection is ordered?

Tags:

Can I somehow "instruct" LINQ to use binary search when the collection that I'm trying to search is ordered. I'm using an ObservableCollection<T>, populated with ordered data, and I'm trying to use Enumerable.First(<Predicate>). In my predicate, I'm filtering by the value of the field my collection's sorted by.

like image 991
luvieere Avatar asked Nov 19 '09 20:11

luvieere


People also ask

Which Method uses to search element in sorted list?

Binary search is an efficient algorithm for finding an item from a sorted list of items.

When should we use LINQ?

LINQ in C# is used to work with data access from sources such as objects, data sets, SQL Server, and XML. LINQ stands for Language Integrated Query. LINQ is a data querying API with SQL like query syntaxes. LINQ provides functions to query cached data from all kinds of data sources.

Can we do binary search in stack?

Any indexed/random-access data structure can be binary searched. So when you say using "just an array", I would say arrays are the most basic/common data structure that a binary search is employed on. Remember to watch for overflow when calculating mid.

What is binary search in C#?

Binary search works on a sorted array. The value is compared with the middle element of the array. If equality is not found, then the half part is eliminated in which the value is not there. In the same way, the other half part is searched. Here is the mid element in our array.


2 Answers

As far as I know, it's not possible with the built-in methods. However it would be relatively easy to write an extension method that would allow you to write something like that :

var item = myCollection.BinarySearch(i => i.Id, 42); 

(assuming, of course, that you collection implements IList ; there's no way to perform a binary search if you can't access the items randomly)

Here's a sample implementation :

public static T BinarySearch<T, TKey>(this IList<T> list, Func<T, TKey> keySelector, TKey key)         where TKey : IComparable<TKey> {     if (list.Count == 0)         throw new InvalidOperationException("Item not found");      int min = 0;     int max = list.Count;     while (min < max)     {         int mid = min + ((max - min) / 2);         T midItem = list[mid];         TKey midKey = keySelector(midItem);         int comp = midKey.CompareTo(key);         if (comp < 0)         {             min = mid + 1;         }         else if (comp > 0)         {             max = mid - 1;         }         else         {             return midItem;         }     }     if (min == max &&         min < list.Count &&         keySelector(list[min]).CompareTo(key) == 0)     {         return list[min];     }     throw new InvalidOperationException("Item not found"); } 

(not tested... a few adjustments might be necessary) Now tested and fixed ;)

The fact that it throws an InvalidOperationException may seem strange, but that's what Enumerable.First does when there's no matching item.

like image 169
Thomas Levesque Avatar answered Sep 28 '22 10:09

Thomas Levesque


The accepted answer is very good.

However, I need that the BinarySearch returns the index of the first item that is larger, as the List<T>.BinarySearch() does.

So I watched its implementation by using ILSpy, then I modified it to have a selector parameter. I hope it will be as useful to someone as it is for me:

public static class ListExtensions {     public static int BinarySearch<T, U>(this IList<T> tf, U target, Func<T, U> selector)     {         var lo = 0;         var hi = (int)tf.Count - 1;         var comp = Comparer<U>.Default;          while (lo <= hi)         {             var median = lo + (hi - lo >> 1);             var num = comp.Compare(selector(tf[median]), target);             if (num == 0)                 return median;             if (num < 0)                 lo = median + 1;             else                 hi = median - 1;         }          return ~lo;     } } 
like image 41
Larry Avatar answered Sep 28 '22 10:09

Larry