C++ sorted vector: Difference between revisions

From Bitpost wiki
(Created page with "The following class extends std::vector, adding sorting support. Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality ...")
 
No edit summary
Line 1: Line 1:
The following class extends std::vector, adding sorting support.  Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality you get from the Standard Template Library.  Check the code comments for more.
The following class extends std::vector, adding sorting support.  Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality you get from the Standard Template Library.  Check the code comments for more.


<pre><code>
<pre>
//-------------------------------------------------------------------//
// Copyright © 2001-2011 A better Software.
//
// Use at your own risk.  :>
//-------------------------------------------------------------------//
 
#if !defined(__SORTED_VECTOR_H)
#define __SORTED_VECTOR_H
 
#include <vector>
#include <algorithm> // For lower_bound




Line 18: Line 29:
//
//
// METHOD 2
// METHOD 2
// Allow batch insertion without sorting with push_unsorted(); then  
// Allow batch insertion without sorting with push_unsorted(); then
// provide an additional call to sort the vector;  before
// provide an additional call to sort the vector;  before
// searching for an item, the vector is always sorted if needed;
// searching for an item, the vector is always sorted if needed;
Line 79: Line 90:
class sorted_vector : public std::vector<T, _A>
class sorted_vector : public std::vector<T, _A>
{
{
typedef std::vector<T> inherited;
    typedef std::vector<T> inherited;


public:
public:


//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
// SetSorted() //
    // SetSorted() //
//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
// I didn't want to override every constructor to set this
    // I didn't want to override every constructor to set this
// member variable, so this function is publicly accessible.
    // member variable, so this function is publicly accessible.
// You should call SetSorted( true ) right after construction.
    // You should call SetSorted( true ) right after construction.
// TO DO: if you feel like it... derive all constructors to avoid  
    // TO DO: if you feel like it... derive all constructors to avoid
// the need for this.  There are 4 last time I checked.
    // the need for this.  There are 4 last time I checked.
//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
void SetSorted( bool bSorted = true ) { m_bSorted = bSorted; }
    void SetSorted( bool bSorted = true ) { m_bSorted = bSorted; }


//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
     // sort()                                                            //
     // sort()                                                            //
//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
     // This function sorts the data as needed.  Call it after repeated calls to
     // This function sorts the data as needed.  Call it after repeated calls to
     // push_unsorted(), or just let other members call it for you on next access.
     // push_unsorted(), or just let other members call it for you on next access.
     // It calls std::sort(), which defaults to using operator<() for
     // It calls std::sort(), which defaults to using operator<() for
     // comparisons.
     // comparisons.
//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
void sort()
    void sort()
{
    {
if ( !m_bSorted )
        if ( !m_bSorted )
{
        {
             std::sort( inherited::begin(), inherited::end() );
             std::sort( inherited::begin(), inherited::end() );
SetSorted();
            SetSorted();
}
        }
}
    }


     // This function is stupid.  binary_search() was a dumb design, STL peeps.
     // This function is stupid.  binary_search() was a dumb design, STL peeps.
bool bContains( const T& t )
    bool bContains( const T& t )
{
    {
if ( !m_bSorted )
        if ( !m_bSorted )
{
        {
sort();
            sort();
}
        }


return std::binary_search( inherited::begin(), inherited::end(), t );
        return std::binary_search( inherited::begin(), inherited::end(), t );
}
    }


     /*const*/ T* lower_bound( const T& key )
     /*const*/ T* lower_bound( const T& key )
Line 154: Line 165:
     // This is the function you want to use most of the time
     // This is the function you want to use most of the time
     // (or the predicate version if you are using object pointers).
     // (or the predicate version if you are using object pointers).
    //
    // NOTE: The STL is guilty of the most fucked up ridiculous behavior.
    // We have to check the result of lower_bound to see if there was a hit.
    // There must be a better way.  For now, this is it.
     //
     //
     // USAGE: it makes most sense to use this function if you have
     // USAGE: it makes most sense to use this function if you have
Line 190: Line 197:
     // using push_unsorted() for each, then calling sort().
     // using push_unsorted() for each, then calling sort().
     //-------------------------------------------------------------------//
     //-------------------------------------------------------------------//
void push_sorted( const T& t )
    void push_sorted( const T& t )
{
    {
if ( !m_bSorted )
        if ( !m_bSorted )
{
        {
sort();
            sort();
}
        }


// Insert at "lower_bound" (the proper sorted location).
        // Insert at "lower_bound" (the proper sorted location).
insert( std::lower_bound( inherited::begin(), inherited::end(), t ), t );
        insert( std::lower_bound( inherited::begin(), inherited::end(), t ), t );
}
    }


//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
// push_unsorted() //
    // push_unsorted() //
//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
// This is similar to push_back(), but in addition, it sets the  
    // This is similar to push_back(), but in addition, it sets the
// unsorted flag.
    // unsorted flag.
//-------------------------------------------------------------------//
    //-------------------------------------------------------------------//
void push_unsorted( const T& t )
    void push_unsorted( const T& t )
{
    {
SetSorted( false );
        SetSorted( false );
         push_back(t);
         push_back(t);
}
    }


     //-------------------------------------------------------------------//
     //-------------------------------------------------------------------//
Line 290: Line 297:


protected:
protected:
bool m_bSorted;
    bool m_bSorted;


};
};


</code></pre>
#endif // __SORTED_VECTOR_H
</pre>

Revision as of 20:38, 21 January 2011

The following class extends std::vector, adding sorting support. Most importantly to me, it can do an actual search in a logical manner, unlike the out-of-the-box functionality you get from the Standard Template Library. Check the code comments for more.

//-------------------------------------------------------------------//
//	Copyright © 2001-2011 A better Software.
//
// Use at your own risk.  :>
//-------------------------------------------------------------------//

#if !defined(__SORTED_VECTOR_H)
#define __SORTED_VECTOR_H

#include <vector>
#include <algorithm>			// For lower_bound


//-------------------------------------------------------------------//
// sorted_vector																		//
//-------------------------------------------------------------------//
// This class is derived from std::vector, and adds sort tracking.
// There are two basic ways to use a sorted vector:
//
//		METHOD 1
//			Always maintain sort order by inserting with push_sorted() -
//			the location of new items is determined before inserting;
//			since the vector remains sorted, this doesn't take too long
//			(although for large batch insertions METHOD 2 is definitely
//			faster);
//
//		METHOD 2
//			Allow batch insertion without sorting with push_unsorted(); then
//			provide an additional call to sort the vector;  before
//			searching for an item, the vector is always sorted if needed;
//
//  Of course you need to provide an operator()< for the type of object
//  you're sorting, if it doesn't have one.  Example:
    /*
        class MyClass
        {
        public:
            bool operator< (const MyClass left) const
            {
                if ( left.m_nMostImportant == m_nMostImportant )
                    return left.m_nLeastImportant < m_nLeastImportant;

                return left.m_nMostImportant < m_nMostImportant;
            }

            int m_nMostImportant;
            int m_nLeastImportant;
        }
    */
//
//  NOTE: C++ doesn't let you use an operator()< for POINTERS.  This
//  breaks down when creating the template code, as you end up with
//  a ref to a ref which is not allowed (or something :P).
//  So if you have a vector of pointers, here's what you have to do:
//
//      FOR NOW, with straight C++, create a less-than functor, then
//      pass that in to the functor versions of the class methods below.
//      Create a functor, aka function object, as follows:
//
//          struct my_class_lessthan
//          {
//              bool operator()(const MyClass* left, const MyClass* right)
//              {
//                  return left->get_timestamp() < right->get_timestamp();
//              }
//          };
//
//      Usage example:
//
//          sorted_vector<MyClass*> svpMC;
//          svpMC.push_unsorted(new MyClass(blah, blah);
//          svpMC.push_unsorted(new MyClass(blah, blah);
//          vpMC.sort( my_class_lessthan() );
//
//      Once C++0x is available, I need to update this class to use
//      a function object wrapper, and allow the user to set it
//      in the constructor, then always use it automatically.
//      http://en.wikipedia.org/wiki/C%2B%2B0x#Polymorphic_wrappers_for_function_objects
//
// FUN NOTE: you can cause an internal compiler error if you leave off
// the part bracketed by #...
//
//		class sorted_vector : public std::vector#<T>#
//
//-------------------------------------------------------------------//
template<class T, class _A = std::allocator<T> >
class sorted_vector : public std::vector<T, _A>
{
    typedef std::vector<T> inherited;

public:

    //-------------------------------------------------------------------//
    // SetSorted()																			//
    //-------------------------------------------------------------------//
    // I didn't want to override every constructor to set this
    // member variable, so this function is publicly accessible.
    // You should call SetSorted( true ) right after construction.
    // TO DO: if you feel like it... derive all constructors to avoid
    // the need for this.  There are 4 last time I checked.
    //-------------------------------------------------------------------//
    void SetSorted( bool bSorted = true ) { m_bSorted = bSorted; }

    //-------------------------------------------------------------------//
    // sort()                                                            //
    //-------------------------------------------------------------------//
    // This function sorts the data as needed.  Call it after repeated calls to
    // push_unsorted(), or just let other members call it for you on next access.
    // It calls std::sort(), which defaults to using operator<() for
    // comparisons.
    //-------------------------------------------------------------------//
    void sort()
    {
        if ( !m_bSorted )
        {
            std::sort( inherited::begin(), inherited::end() );
            SetSorted();
        }
    }

    // This function is stupid.  binary_search() was a dumb design, STL peeps.
    bool bContains( const T& t )
    {
        if ( !m_bSorted )
        {
            sort();
        }

        return std::binary_search( inherited::begin(), inherited::end(), t );
    }

    /*const*/ T* lower_bound( const T& key )
    {
        if ( !m_bSorted )
          sort();

        typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key );

        if (it==inherited::end())
            return 0;

        /*const*/ T* t = &(*it);
        return t;
    }
    typename std::vector<T>::iterator lower_bound_it( const T& key )
    {
        if ( !m_bSorted )
          sort();

        typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key );
        return it;
    }


    //-------------------------------------------------------------------//
    // find_it_or_fail()                                                 //
    //-------------------------------------------------------------------//
    // This function takes the given object and determines if there is
    // a match in the vector.  It returns an iterator to the actual
    // object in the vector, if found.  Otherwise returns std::vector::end().
    //
    // This is the function you want to use most of the time
    // (or the predicate version if you are using object pointers).
    //
    // USAGE: it makes most sense to use this function if you have
    // an object with a key, other member variables, and operator<()
    // that uses the key to test for equality.  You then set up a dummy
    // "search" object with the key set to the search value, call the
    // function, and use the result to extract the additional information
    // from the object.
    //
    // WARNING: if you change the key value of any object in the vector,
    // you have unsorted the array without marking it as such.  Make sure
    // you call SetSorted(false) where appropriate.
    //
    //-------------------------------------------------------------------//
     typename std::vector<T>::iterator find_it_or_fail( const T& key )
     {
         typename std::vector<T>::iterator it = lower_bound_it( key );
         if ((*it)==key)
             return it;
         else
             return inherited::end();
     }

    //-------------------------------------------------------------------//
    // push_sorted()																		//
    //-------------------------------------------------------------------//
    // This is used to insert into a vector that always remains sorted.
    // Because we have a sorted vector, finding the insertion location
    // with std::lower_bound() is relatively cheap.
    //
    // If you have multiple insertions, consider
    // using push_unsorted() for each, then calling sort().
    //-------------------------------------------------------------------//
    void push_sorted( const T& t )
    {
        if ( !m_bSorted )
        {
            sort();
        }

        // Insert at "lower_bound" (the proper sorted location).
        insert( std::lower_bound( inherited::begin(), inherited::end(), t ), t );
    }

    //-------------------------------------------------------------------//
    // push_unsorted()																	//
    //-------------------------------------------------------------------//
    // This is similar to push_back(), but in addition, it sets the
    // unsorted flag.
    //-------------------------------------------------------------------//
    void push_unsorted( const T& t )
    {
        SetSorted( false );
        push_back(t);
    }

    //-------------------------------------------------------------------//
    // operator=()																	//
    //-------------------------------------------------------------------//
    // This allows us to set the sorted_vector from a std::vector.
    //-------------------------------------------------------------------//
    sorted_vector<T>& operator=(std::vector<T>& v)
    {
        typename std::vector<T>::iterator it;
        for (it= v.begin(); it != v.end(); ++it)
            push_unsorted((*it));
        return this;
    }

    // CALLS WHERE YOU PROVIDE THE FUNCTOR OR FUNCTION POINTER
    // If you need to use a predicate sort function, ALWAYS use these methods
    // instead of the non-functor versions.
    // NOTE: UPDATE THIS SHITTINESS when C++0x polymorphic function wrappers are available.
   template<class _Pr> inline
    void sort( _Pr pr )
    {
        if ( !m_bSorted )
        {
                  std::sort( inherited::begin(), inherited::end(), pr );
            SetSorted();
        }
    }
    template<class _Pr> inline
    /*const*/ T* lower_bound( const T& key, _Pr pr )
    {
        if ( !m_bSorted )
        {
          std::sort( inherited::begin(), inherited::end(), pr );
          SetSorted();
        }

        typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key, pr );

        if (it==inherited::end())
            return 0;

        /*const*/ T* t = &(*it);
        return t;
    }
    template<class _Pr> inline
     typename std::vector<T>::iterator lower_bound_it( const T& key, _Pr pr )
     {
         if ( !m_bSorted )
         {
             std::sort( inherited::begin(), inherited::end(), pr );
             SetSorted();
         }
         typename std::vector<T>::iterator it = std::lower_bound( inherited::begin(), inherited::end(), key, pr );
         return it;
     }
     template<class _Pr> inline
     void push_sorted( const T& t, _Pr pr )
     {
         if ( !m_bSorted )
         {
             std::sort( inherited::begin(), inherited::end(), pr );
             SetSorted();
         }

         // Insert at "lower_bound" (the proper sorted location).
         insert( std::lower_bound( inherited::begin(), inherited::end(), t, pr ), t );
     }
     template<class _Pr> inline
      typename std::vector<T>::iterator find_it_or_fail( const T& key, _Pr pr )
      {
          typename std::vector<T>::iterator it = lower_bound_it( key, pr );
          if ((*it)==key)
              return it;
          else
              return inherited::end();
      }

protected:
    bool m_bSorted;

};

#endif // __SORTED_VECTOR_H