Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 variadic templates: return tuple from variable list of vectors

I want to write something similar to python zip (http://docs.python.org/2/library/functions.html). zip should take in a variable number of vectors of different types and it returns a vector of tuples, truncated to the length of the shortest input.

E.g.

x = [1, 2, 3]
v = ['a', 'b']

I want the output to be a vector of [ <1, 'a'>, <2, 'b'>]

How do I do this in C++11?

like image 953
duli Avatar asked Jun 17 '13 21:06

duli


2 Answers

Doing this eagerly and only with copying is pretty easy:

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

template<class... Ts>
std::vector<std::tuple<Ts...>> zip(std::vector<Ts> const&... vs){
    auto lo = std::min({vs.size()...});
    std::vector<std::tuple<Ts...>> v;
    v.reserve(lo);
    for(unsigned i = 0; i < lo; ++i)
        v.emplace_back(vs[i]...);
    return v;
}

Live example.

With perfect forwarding and allowing moves out of the vector, it becomes just a bit more complicated, mostly due to the helpers:

#include <vector>
#include <tuple>
#include <algorithm>
#include <type_traits>

template<class T>
using Invoke = typename T::type;

template<class T>
using Unqualified = Invoke<std::remove_cv<Invoke<std::remove_reference<T>>>>;

template<class T>
using ValueType = typename Unqualified<T>::value_type;

template<class T>
T const& forward_index(std::vector<T> const& v, unsigned i){
    return v[i];
}

template<class T>
T&& forward_index(std::vector<T>&& v, unsigned i){
    return std::move(v[i]);
}

template<class... Vs>
std::vector<std::tuple<ValueType<Vs>...>> zip(Vs&&... vs){
    auto lo = std::min({vs.size()...});
    std::vector<std::tuple<ValueType<Vs>...>> v;
    v.reserve(lo);
    for(unsigned i = 0; i < lo; ++i)
        v.emplace_back(forward_index(std::forward<Vs>(vs), i)...);
    return v;
}

Live example.

like image 160
Xeo Avatar answered Oct 27 '22 15:10

Xeo


The following template function may be a good starting point.

template <typename ...Types>
auto zip(const std::vector<Types>&... values)
    -> std::vector<std::tuple<Types...>>
{
    auto size = std::min({ values.size()... });
    std::vector<std::tuple<Types...>> result;
    for (std::size_t i = 0; i != size; ++i) {
        result.emplace_back(values[i]...);
    }
    return result;
}
like image 5
nosid Avatar answered Oct 27 '22 15:10

nosid