5

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);
einpoklum
  • 118,144
  • 57
  • 340
  • 684
Floop
  • 451
  • 4
  • 10
  • 1
    The naming and namespace of the function suggests to me that it doesn't create a new data structure. Are you sure it doesn't already operate on the names and ages data structures directly? If so, unzipping would be a no-op – stefan May 23 '15 at 21:08
  • @stefan You are right indeed! Why would I ever need unzip, if I always have all the original zipped vectors! I was just confused by immutable Haskell lists. Thank you, can you please make it an answer, so I can mark it correct? – Floop May 23 '15 at 23:09

1 Answers1

10

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.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Casey
  • 41,449
  • 7
  • 95
  • 125
  • Indeed. Even more convenient than having `unzip` is making `zip` do the right thing in the first place. :-) Thanks, @Casey. – Eric Niebler May 24 '15 at 20:39
  • I notice there is `ranges::get`, found [_here_](https://ericniebler.github.io/range-v3/group__group-core.html#ga18e17f37e6741f3537ad02cd748a9da2). But it only now takes a range, it would be nice if `ranges::get` can also take tuple like `get_n` above. [godbolt.org/g/QaiQQn](https://godbolt.org/g/QaiQQn) – sandthorn Mar 11 '18 at 09:05
  • please edit answer for newest range-v3 version. I can't compile this code – Yen Dang Jul 22 '22 at 18:26
  • range-v3 doesn't support C++11 anymore, so I prefer to leave the answer as-is for historical value. https://godbolt.org/z/8KPebKx8E is updated to modern range-v3. – Casey Oct 18 '22 at 22:16