Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IList

Getting the index of an item in a sorted IList

4.56/5 (2 votes)
25 Dec 2011CPOL1 min read 36.6K  
A binary search of a sorted IList to retrieve an index
Part of the code I'm working on is a sorted List<someclass>. It is sorted on a property (key) of the class, and we need to access the list to:
0) See if it contains an entry with a particular key.
1) If it does, retrieve or replace the item.
2) If not, add the item in the correct position (to keep it sorted).

What is desired is a method that will retrieve the index of the existing item or where the item ought to be. The IndexOf method of List is unsuitable for this, plus because we are assuming a sorted list, we can use a binary search in an effort to optimize the search.

Here is an implementation for the simple case where the list contains the same type as the key:

public static bool TryGetIndex<T>
(
  this System.Collections.Generic.IList<T> List
,
  T                                        Sought
,
  out int                                  Index
) where T : System.IComparable<T>
{
  bool result = false ;

  Index = 0 ;

  int lo = 0 ;
  int hi = List.Count - 1 ;

  while ( !result && ( lo <= hi ) )
  {
    Index = lo + ( hi - lo ) / 2 ;

    int diff = Sought.CompareTo ( List [ Index ] ) ;

    if (diff == 0 )
    {
      result = true ;
    }
    else if ( diff < 0 )
    {
      hi = Index - 1 ;
    }
    else
    {
      lo = ++Index ;
    }
  }

  return ( result ) ;
}


However, that is not the situation I described earlier, so I have this version:

public delegate int Comparison<T,U> ( T Op1 , U Op2 ) ;

public static bool TryGetIndex<T,U>
(
  this System.Collections.Generic.IList<U> List
,
  T                                        Sought
,
  Comparison<T,U>                          Comparer
,
  out int                                  Index
)
{
  bool result = false ;

  Index = 0 ;

  int lo = 0 ;
  int hi = List.Count - 1 ;

  while ( !result && ( lo <= hi ) )
  {
    Index = lo + ( hi - lo ) / 2 ;

    int diff = Comparer ( Sought , List [ Index ] ) ;

    if ( diff == 0 )
    {
      result = true ;
    }
    else if ( diff < 0 )
    {
      hi = Index - 1 ;
    }
    else
    {
      lo = ++Index ;
    }
  }

  return ( result ) ;
}


This requires that you provide a method to perform the comparison. Here's an example where I'm trying to find a DataRowView with a particular value for Name:

delegate
(
  string                  Op1
,
  System.Data.DataRowView Op2
)
{
  return ( System.StringComparer.InvariantCultureIgnoreCase.Compare 
  ( 
    Op1 
  , 
    Op2 [ "Name" ]  
  ) ) ;
}


Using this code to find the index of an existing item or the index where a non-existent item belongs allows you to add items where they belong to ensure that the list remains sorted. And the binary search is more efficient than a linear search, especially as the list grows large.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)