Reputation: 10538
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
struct Item1{int id; int data;};
vector<Item1> v1 = {{.id=1,.data=42},{.id=2,.data=69}};
struct Item2{int id; string data;};
vector<Item2> v2 = {{.id=1, .data="banana"},{.id=3,.data="coconut"}};
vector<int> select;
auto ret = ranges::set_intersection(v1,v2,back_inserter(select),{},&Item1::id,&Item2::id);
How do I get the result which should be
[[{.id=1,.data=42}, {.id=1, .data="banana"}]]
?
Upvotes: 0
Views: 90
Reputation: 10538
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
using namespace ranges;
struct Item1{int id; int data;};
vector<Item1> v1 = {{.id=1,.data=42},{.id=2,.data=69}};
struct Item2{int id; string data;};
vector<Item2> v2 = {{.id=1, .data="banana"},{.id=3,.data="coconut"}};
vector<int> select;
auto ret = ranges::set_intersection(
v1 | views::transform(&Item1::id),
v2 | views::transform(&Item2.id),
back_inserter(select)
);
auto rg = select | views::transform([&](int id){return make_tuple(v1[id], v2[id]);});
Upvotes: 0
Reputation: 62874
std::ranges::set_intersection
doesn't do what you want here
If some element is found m times in
[first1, last1)
and n times in[first2, last2)
, the firstmin(m, n)
elements will be copied from the first range to result.
You need a different algorithm, one that will use the values from both ranges. Adapting the possible implementation of set_intersection
template< class I1, class I2, class O, class F, class Comp = std::ranges::less, class Proj1 = std::identity, class Proj2 = std::identity >
concept joinable =
std::input_iterator<I1> &&
std::input_iterator<I2> &&
std::weakly_incrementable<O> &&
std::indirectly_writable<O, std::indirect_result_t<F&, I1, I2>> &&
std::indirect_strict_weak_order<Comp,
std::projected<I1, Proj1>,
std::projected<I2, Proj2>>;
struct join_fn {
template< std::input_iterator I1, std::sentinel_for<I1> S1,
std::input_iterator I2, std::sentinel_for<I2> S2,
std::weakly_incrementable O,
class Comp = std::ranges::less,
std::copy_constructible F,
class Proj1 = std::identity, class Proj2 = std::identity >
requires joinable<I1, I2, O, F, Comp, Proj1, Proj2>
constexpr std::ranges::binary_transform_result<I1, I2, O>
operator()( I1 first1, S1 last1, I2 first2, S2 last2, O result, F binary_op, Comp comp, Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
while (!(first1 == last1 or first2 == last2)) {
if (std::invoke(comp, std::invoke(proj1, *first1), std::invoke(proj2, *first2)))
++first1;
else if (std::invoke(comp, std::invoke(proj2, *first2), std::invoke(proj1, *first1)))
++first2;
else
*result = std::invoke(binary_op, *first1, *first2), ++result, ++first1, ++first2;
}
return {std::ranges::next(std::move(first1), std::move(last1)),
std::ranges::next(std::move(first2), std::move(last2)),
std::move(result)};
}
template< std::ranges::input_range R1, std::ranges::input_range R2,
std::weakly_incrementable O,
class Comp = std::ranges::less,
std::copy_constructible F,
class Proj1 = std::identity, class Proj2 = std::identity >
requires joinable<std::ranges::iterator_t<R1>, std::ranges::iterator_t<R2>,
O, F, Comp, Proj1, Proj2>
constexpr std::ranges::binary_transform_result<std::ranges::borrowed_iterator_t<R1>,
std::ranges::borrowed_iterator_t<R2>, O>
operator()( R1&& r1, R2&& r2, O result, F binary_op, Comp comp = {},
Proj1 proj1 = {}, Proj2 proj2 = {} ) const {
return (*this)(std::ranges::begin(r1), std::ranges::end(r1),
std::ranges::begin(r2), std::ranges::end(r2),
std::move(result), std::move(binary_op), std::move(comp),
std::move(proj1), std::move(proj2));
}
} join;
And then you'll need to put them into a container that can hold the joined type.
struct ItemCombined{ int id; int data1; std::string data2; };
ItemCombined combine(Item1, Item2); // to implement
Finally you can
std::vector<Item1> v1 = { {.id=1, .data=42 }, {.id=2, .data=69 } };
std::vector<Item2> v2 = { {.id=1, .data="banana"s }, {.id=3, .data="coconut"s } };
std::vector<ItemCombined> select;
join(v1,v2,std::back_inserter(select),{},combine,&Item1::id,&Item2::id);
Upvotes: 1