Custom Sorting a vector of tuples

前端 未结 4 415
自闭症患者
自闭症患者 2021-02-04 04:22

I have a vector of tuples like

vector> v;

I believe that when the default comparison kicks in for tuple types, i

相关标签:
4条回答
  • 2021-02-04 04:56

    You can do it like this

    #include <tuple>
    #include <vector>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    
    vector<tuple<int, float, char>> v;
    
    int main() {
      v.push_back(std::make_tuple(1,1.2,'c'));
      v.push_back(std::make_tuple(1,1.9,'e'));
      v.push_back(std::make_tuple(1,1.7,'d'));
    
      sort(v.begin(),v.end(),
           [](const tuple<int,float,char>& a,
           const tuple<int,float,char>& b) -> bool
           {
             return std::get<1>(a) > std::get<1>(b);
           });
    
      cout << std::get<2>(v[0]) << endl;
      return 0;
    }
    
    0 讨论(0)
  • 2021-02-04 05:04

    So here is the simplest way I learned how to do it (and I think it is even more straightforward than the main answer). This will work on any type in the tuple that can be compared with <.

    Here is the sort function TupleLess, which takes advantage of some C style syntax:

     #include <string>
     #include <vector>
     #include <tuple>      
    
     template<int index> struct TupleLess
     {
        template<typename Tuple>
        bool operator() (const Tuple& left, const Tuple& right) const
        {
            return std::get<index>(left) < std::get<index>(right);
        }
    };
    
    int main(){
        //Define a Person tuple:    
        using Person = std::tuple<std::string, std::string, std::string>;
    
        //Create some Person tuples and add to a vector
        Person person1 = std::make_tuple("John", "Smith", "323 Fake Street");
        Person person2 = std::make_tuple("Sheila", "Henrod", "784 Unreal Avenue");
        Person person3 = std::make_tuple("Mitch", "LumpyLumps", "463 Falsified Lane");
    
        std::vector<Person> vPerson = std::vector<Person>();
        vPerson.push_back(person1);
        vPerson.push_back(person2);
        vPerson.push_back(person3);
    
        //Sort by index at position 2
        std::sort(vPerson.begin(), vPerson.end(), TupleLess<2>());
    }
    
    0 讨论(0)
  • 2021-02-04 05:15

    There are many ways to do that, the one I use boils down to declaring a custom comparison object, actually the following

    // Functor to compare by the Mth element
    template<int M, template<typename> class F = std::less>
    struct TupleCompare
    {
        template<typename T>
        bool operator()(T const &t1, T const &t2)
        {
            return F<typename tuple_element<M, T>::type>()(std::get<M>(t1), std::get<M>(t2));
        }
    };
    

    It works for tuples of arbitrary length (avoiding variadic templates - even though it's fairly easy and safer to do it the variadic way because you can declare the arguments of operator() as tuples of arbitrary length) and also works for pairs and you can pass it a custom comparison function/object but uses < operator as the default policy. An example usage would be

    int main()
    {
        vector<tuple<int, string>> v;
        v.push_back(make_tuple(1, "Hello"));
        v.push_back(make_tuple(2, "Aha"));
    
        std::sort(begin(v), end(v), TupleCompare<0>());
        return 0;
    }
    

    there is ofcourse a more modern way, by using lambdas, so the sorting line would be

    std::sort(begin(v), end(v), 
        [](tuple<int, string> const &t1, tuple<int, string> const &t2) {
            return get<0>(t1) < get<0>(t2); // or use a custom compare function
        }
    );
    

    I believe it's worth making a function object for this (avoids a lot of boilerplate code) and go with the first approach


    EDIT

    As Yakk's comment (on c++1y) became standard compliant (c++14), we demonstrate below the case for generic lambdas

    std::sort(begin(v), end(v), [](auto const &t1, auto const &t2) {
            return get<0>(t1) < get<0>(t2); // or use a custom compare function
    });
    

    which greatly matches the mechanics of TupleCompare since the operator() is templated as well.

    0 讨论(0)
  • 2021-02-04 05:19

    Yes, you can define custom sorting in C++ when you need it. I assume you need it for std::sort, right? Look at the documentation of std::sort, at the second version of the algorithm to be precise, the one which takes the comp argument.

    You have to define a less-than functor, something like this:

    struct CustomLessThan
    {
        bool operator()(tuple<T1, T2, T3> const &lhs, tuple<T1, T2, T3> const &rhs) const
        {
            return std::get<1>(lhs) < std::get<1>(rhs);
        }
    };
    

    And then use it in std::sort:

    std::sort(v.begin(), v.end(), CustomLessThan());
    

    In C++11, you can make the code much shorter by using a lambda instead of creating a named struct. The examples given at cppreference.com also show this technique.

    0 讨论(0)
提交回复
热议问题