Jonathan Mee
Jonathan Mee

Reputation: 38919

transform with No Output

I have two vectors of the same size. I need to modify each element of one vector by the contents of the other vector. Is there a standard algorithm that accomplishes this without assigning back to the original vector?

So say I want to modify all the elements of vector foo by the corresponding element of vector bar. I can use transform:

transform(foo.begin(), foo.end(), bar.begin(), foo.begin(), [](auto& a, const auto& b){a.func(b); return a;});

But that assignment back to foo is unnecessary. Alternatively I can write my own function:

auto a = foo.begin();
auto b = bar.begin();

while(a != foo.end()){
    (a++)->func(*(b++));
}

But I'd rather not reinvent the wheel if there's something already there.

Upvotes: 1

Views: 1652

Answers (3)

mariusm
mariusm

Reputation: 1668

Small utility function template:

template <typename Input1, typename Input2, typename BinaryOperation>
void zip(Input1 b1, Input1 e1, Input2 b2, BinaryOperation binOp) {
    while (b1 != e1) binOp(*b1++, *b2++);
}

Usage:

zip(begin(foo), end(foo), begin(bar), [](auto& a, const auto& b){ a.func(b); });

Upvotes: 2

Jonathan Mee
Jonathan Mee

Reputation: 38919

The cleanest answer to this seems to be using an index rather than an iterator.

for(auto i = 0; i < foo.size(); ++i) foo[i].func(bar[i]);

It's only requirement is analogous to that of any multiple input iterator algorithm: All containers must have at least the same range as indicated by the first pair of iterators. Here meaning that all containers should be of size greater than or equal to foo.size(). Unlike the abuse of transform this can be extended to support as many bracket-operator-accessible containers as desired:

for(auto i = 0; i < foo.size(); ++i) foo[i].func(bar[i], baz[i], buz[i]);

Upvotes: 0

m.s.
m.s.

Reputation: 16334

What you are asking for is basically a std::for_each for binary functions. Since there is no such thing (yet), you could do one of the following:

1) Implement a generic version of such a for_each for binary functions:

(code taken from https://groups.google.com/a/isocpp.org/d/msg/std-proposals/fNqx2TR-ju8/MIqPwgSoWukJ)

#include <vector>
#include <iostream>

template<class InputIt1, class InputIt2, class BinaryFunction>
BinaryFunction for_each(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryFunction f)
{
    for (; first1 != last1; ++first1, ++first2) {
        f(*first1, *first2);
    }
    return f;
}

struct Foo
{
    Foo() : k(-1){}
    int k;
    void func(int i){k=i;}
};

int main() {

    std::vector<Foo> foo(5);
    std::vector<int> bar(5,55);

    for_each(foo.begin(), foo.end(), bar.begin(), [](auto& a, const auto& b){a.func(b); return a;});

    for(auto f : foo)
    {
        std::cout << f.k << std::endl;
    }
    return 0;
}

live on coliru

2) Use boost::zip_iterator and the existing std::for_each:

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

#include <boost/iterator/zip_iterator.hpp>

struct Foo
{
    Foo() : k(-1){}
    int k;
    void func(int i){k=i;}
};

template<typename... Iterators>
auto zip(Iterators... its)
{
    return boost::make_zip_iterator(boost::make_tuple(its...));
}

int main() {

    std::vector<Foo> foo(5);
    std::vector<int> bar(5,55);

    auto zip_func = [](auto&& t){t.template get<0>().func(t.template get<1>());};
    std::for_each(
        zip(foo.begin(), bar.begin()),
        zip(foo.end(), bar.end()),
        zip_func
    );

    for(auto f : foo)
    {
        std::cout << f.k << std::endl;
    }
    return 0;
}

live on coliru

Upvotes: 2

Related Questions