2

I have below data structure,

typedef vector< tuple<int,int,int> > vector_tuple;

In vector i am storing tuple<value,count,position>

I want to sort my vector based on count, If count is same then based on position sort the vector.

structure ordering
{
  bool ordering()(....)
  {
    return /// ?
  }

};
int main()
{ 
    std::vector<int> v1{1,1,1,6,6,5,4,4,5,5,5};
    std::vector<int> v2(v1);
    vector_tuple vt;
    std::tuple<int,int,int> t1;
    std::vector<int>::iterator iter;
    int sizev=v1.size();
    for(int i=0; i < sizev ; i++)
    {
         auto countnu = count(begin(v2),end(v2),v1[i]);  
         if(countnu > 0)
         {
           v2.erase(std::remove(begin(v2),end(v2),v1[i]),end(v2));
           auto t = std::make_tuple(v1[i], countnu, i);
           vt.push_back(t);
         }
     }
     sort(begin(vt),end(vt),ordering(get<1>vt); // I need to sort based on count and if count is same, sort based on position.


     for (int i=0; i < vt.size(); i++)
     {

        cout << get<0>(vt[i]) << " " ;
        cout << get<1>(vt[i]) << " " ;
        cout << get<2>(vt[i]) << " \n" ;
     }

}

3 Answers 3

6

Your compare method should look like:

auto ordering = [](const std::tuple<int, int, int>& lhs,
                   const std::tuple<int, int, int>& rhs) {
    return std::tie(std::get<1>(lhs), std::get<2>(lhs))
         < std::tie(std::get<1>(rhs), std::get<2>(rhs));
};

std::sort(std::begin(vt), std::end(vt), ordering);
Sign up to request clarification or add additional context in comments.

1 Comment

Always prefer std::tie to hand-rolled comparison functions. It always results in 100% correct and optimal code. +1
3

All credit to Jarod42 for the std::tie answer.

Now let's make it generic by creating a variadic template predicate:

template<std::size_t...Is>
struct tuple_parts_ascending
{
    template<class...Ts>
    static auto sort_order(const std::tuple<Ts...>& t)
    {
        return std::tie(std::get<Is>(t)...);
    }

    template<class...Ts>
    bool operator()(const std::tuple<Ts...>& l,
                    const std::tuple<Ts...>& r) const
    {
        return sort_order(l) < sort_order(r);
    }
};

which we can invoke thus:

sort(begin(vt),end(vt),tuple_parts_ascending<1,2>());

Full Code:

#include <vector>
#include <algorithm>
#include <tuple>
#include <iostream>

using namespace std;

typedef std::vector< std::tuple<int,int,int> > vector_tuple;

template<std::size_t...Is>
struct tuple_parts_ascending
{
    template<class...Ts>
    static auto sort_order(const std::tuple<Ts...>& t)
    {
        return std::tie(std::get<Is>(t)...);
    }

    template<class...Ts>
    bool operator()(const std::tuple<Ts...>& l,
                    const std::tuple<Ts...>& r) const
    {
        return sort_order(l) < sort_order(r);
    }
};

int main()
{
    std::vector<int> v1{1,1,1,6,6,5,4,4,5,5,5};
    std::vector<int> v2(v1);
    vector_tuple vt;
    std::tuple<int,int,int> t1;
    std::vector<int>::iterator iter;
    int sizev=v1.size();
    for(int i=0; i < sizev ; i++)
    {
        auto countnu = count(begin(v2),end(v2),v1[i]);
        if(countnu > 0)
        {
            v2.erase(std::remove(begin(v2),end(v2),v1[i]),end(v2));
            auto t = std::make_tuple(v1[i], countnu, i);
            vt.push_back(t);
        }
    }
    sort(begin(vt),end(vt),tuple_parts_ascending<1,2>());


    for (int i=0; i < vt.size(); i++)
    {

        cout << get<0>(vt[i]) << " " ;
        cout << get<1>(vt[i]) << " " ;
        cout << get<2>(vt[i]) << " \n" ;
    }

}

Expected results:

6 2 3
4 2 6
1 3 0
5 4 5

Going further, we could make this operation a little more generic and 'library-worthy' by allowing the ordering predicate and the indices to be passed as parameters (this solution required c++14):

namespace detail {
    template<class Pred, std::size_t...Is>
    struct order_by_parts
    {
        constexpr
        order_by_parts(Pred&& pred)
        : _pred(std::move(pred))
        {}

        template<class...Ts>
        constexpr
        static auto sort_order(const std::tuple<Ts...>& t)
        {
            return std::tie(std::get<Is>(t)...);
        }

        template<class...Ts>
        constexpr
        bool operator()(const std::tuple<Ts...>& l,
                        const std::tuple<Ts...>& r) const
        {
            return _pred(sort_order(l), sort_order(r));
        }

    private:
        Pred _pred;
    };
}

template<class Pred, size_t...Is>
constexpr
auto order_by_parts(Pred&& pred, std::index_sequence<Is...>)
{
    using pred_type = std::decay_t<Pred>;
    using functor_type = detail::order_by_parts<pred_type, Is...>;
    return functor_type(std::forward<Pred>(pred));
}

Now we can sort like so:

sort(begin(vt),end(vt),
     order_by_parts(std::less<>(),                  // use predicate less<void>
                    std::index_sequence<1, 2>()));  // pack indices into a sequence

Comments

1

Using lambdas:

std::sort(std::begin(vt),std::end(vt),[](const auto& l,const auto& r){
    if(std::get<1>(l)== std::get<1>(r)){
         return std::get<2>(l) < std::get<2>(r);
    }
    return std::get<1>(l) < std::get<1>(r);
}); 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.