sortingc++11boost-rangerange-v3

Sorting Range-v3-zipped containers - can I unzip?


Is it possible to unzip previously zipped vectors using the C++ Range-v3 library? I would expect it to behave similarly to Haskell's unzip function or Python's zip(*list).

It would be convenient, for instance, when sorting a vector by values of another vector:

using namespace ranges;

std::vector<std::string> names {"john", "bob", "alice"};
std::vector<int>         ages  {32,     19,    35};

// zip names and ages
auto zipped = view::zip(names, ages);
// sort the zip by age
sort(zipped, [](auto &&a, auto &&b) {
  return std::get<1>(a) < std::get<1>(b);
});
// put the sorted names back into the original vector
std::tie(names, std::ignore) = unzip(zipped);

Solution

  • When passed container arguments, view::zip in range-v3 creates a view consisting of tuples of references to the original elements. Passing the zipped view to sort sorts the elements in place. I.e., this program:

    #include <vector>
    #include <string>
    #include <iostream>
    
    #include <range/v3/algorithm.hpp>
    #include <range/v3/view.hpp>
    
    using namespace ranges;
    
    template <std::size_t N>
    struct get_n {
      template <typename T>
      auto operator()(T&& t) const ->
        decltype(std::get<N>(std::forward<T>(t))) {
          return std::get<N>(std::forward<T>(t));
      }
    };
    
    namespace ranges {
    template <class T, class U>
    std::ostream& operator << (std::ostream& os, common_pair<T, U> const& p) {
      return os << '(' << p.first << ", " << p.second << ')';
    }
    }
    
    int main() {
      std::vector<std::string> names {"john", "bob", "alice"};
      std::vector<int>         ages  {32,     19,    35};
    
      auto zipped = view::zip(names, ages);
      std::cout << "Before: Names: " << view::all(names) << '\n'
                << "         Ages: " << view::all(ages) << '\n'
                << "       Zipped: " << zipped << '\n';
      sort(zipped, less{}, get_n<1>{});
      std::cout << " After: Names: " << view::all(names) << '\n'
                << "         Ages: " << view::all(ages) << '\n'
                << "       Zipped: " << zipped << '\n';
    }
    

    Outputs:

    Before: Names: [john,bob,alice]
             Ages: [32,19,35]
           Zipped: [(john, 32),(bob, 19),(alice, 35)]
     After: Names: [bob,john,alice]
             Ages: [19,32,35]
           Zipped: [(bob, 19),(john, 32),(alice, 35)]
    

    Live Example on Coliru.