Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine two or more vectors of arbitrary types in C++

I've got the following code that combines two vectors of arbitrary types into a combinatorial, i.e. std::vector<std::tuple<A, B>>.

template<class A, class B>
std::vector<std::tuple<A, B>> combine(const std::vector<A>& a, const std::vector<B>& b) {

    const auto combine_parts_ = [](const A& x, const B& y) {
        auto result = std::tuple_cat(std::make_tuple(x), std::make_tuple(y));
        return result;
    };

    std::vector<std::tuple<A, B>> results;

    for (const auto& x : a) {
        for (const auto& y : b) {
            results.push_back(combine_parts_(x, y));
        }
    }

    return results;
}

However, I'm unclear how to extend that to an arbitrary number of types/vectors. I don't care about duplicate types; in fact there may be two or more sets of the same type involved. That's okay.

Some example use cases, for instance:

const auto combinations = combine(
    std::vector<int>({1,2,3})
    , std::vector<int>({1,2,3})
);
const auto combinations2 = combine(
    std::vector<int>({1,2,3})
    , std::vector<int>({1,2,3})
    , std::vector<bool>({true,false})
);
const auto combinations3 = combine(
    std::vector<int>({1,2,3})
    , std::vector<int>({1,2,3})
    , std::vector<bool>({true,false})
    , std::vector<char>({'a','b','c','d','e'})
);

Chiefly, what I want to do is avoid the ugly nested for loop. At the same time, I want to combine some unit testing combinatorial use cases in order to work with the resulting std::tuple<...> as the test case.

Note, I am not talking about permutations of a homogeneous set here. That's been a point of confusion from prior questions.

I think it might have something to do with templates, variadics, std::tuple_cat, somewhere along the way, but I don't know.

Thoughts? Suggestions?

like image 466
mwpowellhtx Avatar asked Nov 08 '18 22:11

mwpowellhtx


People also ask

How do you combine two vectors?

The concatenation of vectors can be done by using combination function c. For example, if we have three vectors x, y, z then the concatenation of these vectors can be done as c(x,y,z). Also, we can concatenate different types of vectors at the same time using the same same function.

Can a vector store multiple data types?

The easiest way to store multiple types in the same vector is to make them subtypes of a parent class, wrapping your desired types in classes if they aren't classes already.

Can vector store different types?

Vectors allow you to store more than one value in a single data structure that puts all the values next to each other in memory. Vectors can only store values of the same type.

Can we append two vectors in C++?

How to join two Vectors using STL in C++? Given two vectors, join these two vectors using STL in C++. Approach: Joining can be done with the help of set_union() function provided in STL.


1 Answers

If you want to compute the Cartesian product of heterogeneous vectors, you may do something like:

template <std::size_t N>
bool increase(const std::array<std::size_t, N>& sizes, std::array<std::size_t, N>& it)
{
    for (std::size_t i = 0; i != N; ++i) {
        const std::size_t index = N - 1 - i;
        ++it[index];
        if (it[index] >= sizes[index]) {
            it[index] = 0;
        } else {
            return true;
        }
    }
    return false;
}

template <typename F, std::size_t ... Is, std::size_t N, typename Tuple>
void apply_impl(F&& f,
                std::index_sequence<Is...>,
                const std::array<std::size_t, N>& it,
                const Tuple& tuple)
{
    f(std::get<Is>(tuple)[it[Is]]...);
}

template <typename F, typename ... Ts>
void cartesian_product_apply(F&& f, const std::vector<Ts>&... vs)
{
    constexpr std::size_t N = sizeof...(Ts);
    std::array<std::size_t, N> sizes{{vs.size()...}};
    std::array<std::size_t, N> it{};

    do {
        apply_impl(f, std::index_sequence_for<Ts...>(), it, std::tie(vs...));
    } while (increase(sizes, it));
}

And finally:

template <typename ... Ts>
std::vector<std::tuple<Ts...>> cartesian_product(const std::vector<Ts>&... vs)
{
    std::vector<std::tuple<Ts...>> res;

    cartesian_product_apply([&res](const auto&... args) { res.emplace_back(args...); },
                            vs...);
    return res;
}

With usage similar to:

std::vector<int> v1 = {1, 2, 3};
std::vector<std::string> v2 = {" A "," B "};
std::vector<int> v3 = {4, 5};

const auto res = cartesian_product(v1, v2, v3);
for (const auto& t : res) {
    // ...
}

Demo

like image 200
Jarod42 Avatar answered Oct 09 '22 07:10

Jarod42