Reputation: 157
How can I replace the for loop with a STL algorithm? Here I want to assign the value of vals[i]
in vector vals
to nodeList[i]->val
in nodeList
. And what if I use vals[i] * 2
to replace it?
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
int main()
{
TreeNode node1(1);
TreeNode node2(2);
TreeNode node3(3);
node1.left = &node2;
node1.right = &node3;
vector<TreeNode*> nodeList = {&node1, &node2, &node3};
vector<int> vals = {1, 3, 2};
for (int i = 0; i < vals.size(); i++) {
nodeList[i]->val = vals[i];
// nodeList[i]->val = vals[i] * 2;
}
}
Upvotes: 2
Views: 1266
Reputation: 62686
Your first case is a copy, the second is a transform. The wrinkle is that you need to transform the output iterator.
auto as_vals = std::views::transform(nodeList, std::mem_fn(&Node::val));
std::copy(vals.begin(), vals.end(), as_vals.begin());
std::transform(vals.begin(), vals.end(), as_vals.begin(), [](int i){ return i * 2; });
Or with boost
auto as_val = boost::make_transform_iterator(nodeList.begin(), std::mem_fn(&Node::val));
std::copy(vals.begin(), vals.end(), as_val);
std::transform(vals.begin(), vals.end(), as_val, [](int i){ return i * 2; });
If you don't have access to C++20 or boost, you can write something like
template <typename Iterator, typename T>
class member_iterator
{
Iterator wrapped;
using base_t = typename std::iterator_traits<Iterator>::value_type;
T (base_t::*mem);
public
using value_type = T;
using pointer = T*;
using reference = T&;
using difference_type = typename std::iterator_traits<Iterator>::difference_type;
using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
member_iterator(Iterator wrapped, T (base_t::*mem)) : wrapped(wrapped), mem(mem) {}
T & operator*() const { return (*wrapped).*mem; }
member_iterator& operator++() { ++wrapped; return *this; }
// etc.
};
Upvotes: 2
Reputation: 85341
The standard "algorithm" for for-loops is std::for_each
. It is essentially a wrapper around a for-loop:
std::for_each(begin(nodeList), end(nodeList), [&vals, i = 0](auto& it) mutable {
it->val = vals[i++];
});
Due to the fact that you need to iterate over two collections at once, you still need an index variable (i
in this example), or use std::distance
to compute it in each iteration.
Upvotes: 2
Reputation: 21718
What are you doing here you are iterating over the two containers at once.
While it is possible to have a very neat iteration over a single container in modern C++, there is nothing that I would recommend from the depth of my heart for iterating over the two - not ready yet. This question lists various possible solutions but I am not really sure.
It is possible to try a ranged loop while manually maintaining the second index or iterator:
auto i = vals.begin();
for (auto & node: nodeList) {
node->val = *i++;
}
This is less code and would be faster while iterating over something like std::deque where (unlike for std::vector) direct indexing is expensive. But may be also reasonable to leave the code as is and just wait for the next C++ standard.
Upvotes: 2