Count elements in union of two sets using stl - c++

I have two sets and I want to know how many elements there are at least in one set. It is a function set_union in <algorithm> which writes the union in another set, but I want only the number. Can I find it using stl without saving elements?

I agree with Marshall Clow; I don't believe there is an off-the-shelf algorithm to do this. Here's an idea I've been toying with. It is a simple class that provides a push_back method that just increments a counter. You use it with a std::back_inserter as an output iterator.
#include <initializer_list>
#include <iterator>
#include <iostream>
#include <algorithm>
template <typename T>
class CountingPushBack
{
public:
using value_type = T;
void push_back(T const &) {++count;}
std::size_t get_count() const {return count;}
private:
std::size_t count = 0;
};
int main()
{
std::initializer_list<int> il1 = { 0, 1, 2, 3, 4 };
std::initializer_list<int> il2 = { 0, 2, 4, 6, 8 };
CountingPushBack<int> cp;
std::set_union(il1.begin(), il1.end(),
il2.begin(), il2.end(),
std::back_inserter(cp));
std::cout << cp.get_count() << std::endl;
}

I don't know of such an algorithm. That being said, you can use the guts of set_union to write your own to do that; like this:
#include <iostream>
#include <set>
// Counts the number of elements that would be in the union
template <class Compare, class InputIterator1, class InputIterator2>
size_t set_union_size(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
Compare comp)
{
size_t __result = 0;
for (; first1 != last1;)
{
if (first2 == last2)
return __result + std::distance(first1, last1);
if (comp(*first2, *first1))
{
++__result;
++first2;
}
else
{
++__result;
if (!comp(*first1, *first2))
++first2;
++first1;
}
}
return __result + std::distance(first2, last2);
}
int main () {
std::set<int> s1 = { 0, 1, 2, 3, 4 };
std::set<int> s2 = { 0, 2, 4, 6, 8 };
std::cout
<< set_union_size(s1.begin(), s1.end(), s2.begin(), s2.end(), std::less<int>())
<< std::endl;
}
And this prints 7, which is what you would expect.

Although the solution by SCFrench is fine, it does require a container, while we only need a back_insert_iterator. Here is an example of an implementation.
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
template <typename T>
class count_back_inserter {
size_t &count;
public:
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
typedef std::output_iterator_tag iterator_category;
count_back_inserter(size_t &count) : count(count) {};
void operator=(const T &){ ++count; }
count_back_inserter &operator *(){ return *this; }
count_back_inserter &operator++(){ return *this; }
};
You can use it by passing a size_t variable to the constructor that will be incremented for every element that is 'added' to the 'underlying container'.
int main(){
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = { 3, 4, 5, 6, 7};
size_t count = 0;
set_union(v1.begin(), v1.end(),
v2.begin(), v2.end(),
count_back_inserter<int>(count));
std::cout << "The number of elements in the union is " << count << std::endl;
}

Related

Alternative to std::set_union with additional predicate parameter for merging elements from the intersection

Given two sorted containers and std::set_union, we can provide a predicate to determine when two elements are equal. I would like to provide an additional predicate that will merge the equal elements (the intersection of the containers) and insert the result into the output container.
Please note in the 'Expected output' section below how the vectors for set_union and unknown_func differ.
Is there a single algorithm that emulates the behavior described by the 'Expected output' below? If there are only more complex ways to have this behavior, can you please suggest where I may get started in doing so? Preferably the final solution only makes use of functionality provided by the std/stl libraries.
Sample Code
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
struct CustomStruct{
CustomStruct(const int f1, const int f2) : field_1(f1), field_2(f2) {}
int field_1;
int field_2;
};
void print_vector(const std::string& str, const std::vector<CustomStruct>& vec){
std::cout << str << std::endl;
for (const auto& val: vec){
std::cout<< val.field_1 << ", " << val.field_2 << std::endl;
}
}
int main()
{
std::vector<CustomStruct> vec_a;
std::vector<CustomStruct> vec_b;
std::vector<CustomStruct> vec_set_union;
std::vector<CustomStruct> vec_unknown_func;
for (int i = 0; i < 4; ++i){ vec_a.emplace_back(i, 2); }
for (int i = 2; i < 4; ++i){ vec_b.emplace_back(i, 3); }
print_vector("VEC_A", vec_a);
print_vector("VEC_B", vec_b);
const auto compare = [](const CustomStruct& lhs, const CustomStruct& rhs){
return lhs.field_1 < rhs.field_1;
};
std::set_union(vec_a.begin(), vec_a.end(),
vec_b.begin(), vec_b.end(),
std::back_inserter(vec_set_union),
compare
);
print_vector("VEC_SET_UNION", vec_set_union);
const auto merge_duplicate = [](const CustomStruct& lhs, const CustomStruct& rhs){
return CustomStruct(lhs.field_1, lhs.field_2 + (rhs.field_2*rhs.field_2));
};
// std::unknown_func(vec_a.begin(), vec_a.end(),
// vec_b.begin(), vec_b.end(),
// std::back_inserter(vec_unknown_func),
// compare,
// merge_duplicate
// );
// THE COMMENTED CODE ABOVE WOULD NEED TO ALLOW 'VEC_UNKNOWN_FUNC' to have
// the 'Expected output' supplied as part of this question
print_vector("VEC_UNKNOWN_FUNC", vec_unknown_func);
}
Expected output
VEC_A
0, 2
1, 2
2, 2
3, 2
VEC_B
2, 3
3, 3
VEC_SET_UNION
0, 2
1, 2
2, 2
3, 2
VEC_UNKNOWN_FUNC
0, 2
1, 2
2, 11
3, 11
Thanks for your time, let me know if I can give further clarifications.
As #Useless suggests in the comments, to do extra things over an <algorithm>, you should write something based on that algorithm.
Adapted from the possible implementation:
template<class InputIt1, class InputIt2,
class OutputIt, class Compare,
class BinaryOp>
OutputIt set_union_transform(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
OutputIt d_first, Compare comp,
BinaryOp binary_op)
{
for (; first1 != last1; ++d_first) {
if (first2 == last2)
return std::copy(first1, last1, d_first);
if (comp(*first2, *first1)) {
*d_first = *first2++;
} else if (comp(*first1, *first2)) {
*d_first = *first1++;
} else {
*d_first = binary_op(*first1++, *first2++);
}
}
return std::copy(first2, last2, d_first);
}
Just bashing at the keyboard but I think you want something like this:
std::vector<CustomStruct> vec_set_intersection1;
std::vector<CustomStruct> vec_set_intersection2;
// Find the duplicate objects in the first vector
std::set_intersection(vec_a.begin(), vec_a.end(),
vec_b.begin(), vec_b.end(),
std::back_inserter(vec_set_intersection1),
compare);
// Find the duplicate objects in the second vector
std::set_intersection(vec_b.begin(), vec_b.end(),
vec_a.begin(), vec_a.end(),
std::back_inserter(vec_set_intersection2),
compare);
// Apply the transformation
std::transform(vec_set_intersection1.begin(), vec_set_intersection1.end(),
vec_set_intersection2.begin(), vec_set_intersection2.end(),
std::back_inserter(vec_unknown_func),
merge_duplicate);

Is there an elegant possibility to include several sets into a multimap?

I have several sets of the same type:
std::set< TDate> spieltagDatum;
I would like to include all of them into a multimap of this type:
std::multimap<TDate, int> ereignis;
Is there an elegant possibility (perhaps with a lambda related function?) to include all members of ONE set into the multimap above not using the iterator mechanism? (The multimap pairs should be enriched with the INT parameter during insert).
I can suggest instead of iterators to use simplified for loop with auto like below.
I used integer TDate just for example, also instead of 123 in my code you may put any function for filling in values of multimap.
Try it online!
#include <map>
#include <set>
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
std::multimap<TDate, int> ereignis;
for (auto & e: spieltagDatum)
ereignis.emplace(e, 123);
}
What do you mean by "not using the iterator mechanism"? (Don't use iterators at your own peril)
As you describe, what you do is to 1) transform (by enrich) and 2) insert, so the answer is std::tranform + std::insert.
#include <algorithm> // transform
#include <cassert>
#include <map>
#include <set>
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
std::set<TDate> ...;
std::multimap<TDate, int> ereignis;
auto enrich = [](auto e){return std::make_pair(e, 123);};
std::transform(
begin(spieltagDatum), end(spieltagDatum),
std::inserter(ereignis, end(ereignis)),
enrich
);
... // repeat for other sets if necessary
assert( ereignis.find(5) != ereignis.end() );
assert( ereignis.find(5)->second == 123 );
}
https://godbolt.org/z/zzYbKK83d
A more declarative option using libraries, based on #prehistoricpenguin answer is:
(IMO it is worth mainly in C++17, where so many of the templates parameters are not really necessary)
#include <cassert>
#include <map>
#include <set>
#include <boost/iterator/transform_iterator.hpp>
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
auto enriched = [](auto it){
return boost::transform_iterator(it, [](auto e){return std::pair(e, 123);});
};
std::multimap ereignis(
enriched(begin(spieltagDatum)),
enriched(end (spieltagDatum))
);
assert( ereignis.find(5) != ereignis.end() );
assert( ereignis.find(5)->second == 123 );
}
https://godbolt.org/z/6ajssjjjP
One possible answer is to write a convert iterator class, then we use the iterator to constructor the multimap instance.
#include <iostream>
#include <iterator>
#include <map>
#include <set>
template <typename KeyT, typename ValT>
class ConvertIter
: public std::iterator<std::forward_iterator_tag, std::pair<KeyT, ValT>> {
using SetIter = typename std::set<KeyT>::iterator;
public:
ConvertIter(SetIter itr, ValT v = ValT{}) : _itr(itr), _val(v) {}
bool operator==(const ConvertIter& other) { return other._itr == _itr; }
bool operator!=(const ConvertIter& other) { return other._itr != _itr; }
std::pair<KeyT, ValT> operator*() const {
return {*_itr, _val};
}
ConvertIter& operator++() {
++_itr;
return *this;
}
ConvertIter& operator++(int) {
++_itr;
return *this;
}
private:
SetIter _itr;
ValT _val;
};
int main() {
using TDate = int;
std::set<TDate> spieltagDatum = {3, 5, 7};
std::multimap<TDate, int> ereignis(
ConvertIter<TDate, int>(spieltagDatum.begin(), 123),
ConvertIter<TDate, int>(spieltagDatum.end()));
for (auto [date, val] : ereignis) {
std::cout << "[" << date << "," << val << "]" << std::endl;
}
return 0;
}
Demo:
https://godbolt.org/z/cr98f15jq

How to find the indices of matching elements of sorted containers?

I'm trying to get the indices of one container where the elements match. Both containers are sorted in ascending order. Is there an algorithm or combo of algorithms that would place the indices of matching elements of sorted containers into another container?
I've coded an algorithm already, but was wondering if this has been coded before in the stl in some way that I didn't think of?
I would like the algorithm to have a running complexity comparable to the one I suggested, which I belive is O(min(m, n)).
#include <iterator>
#include <iostream>
template <typename It, typename Index_it>
void get_indices(It selected_it, It selected_it_end, It subitems_it, It subitems_it_end, Index_it indices_it)
{
auto reference_it = selected_it;
while (selected_it != selected_it_end && subitems_it != subitems_it_end) {
if (*selected_it == *subitems_it) {
*indices_it++ = std::distance(reference_it, selected_it);
++selected_it;
++subitems_it;
}
else if (*selected_it < *subitems_it) {
++selected_it;
}
else {
++subitems_it;
}
}
}
int main()
{
int items[] = { 1, 3, 6, 8, 13, 17 };
int subitems[] = { 3, 6, 17 };
int indices[std::size(subitems)] = {0};
auto selected_it = std::begin(items), it = std::begin(subitems);
auto indices_it = std::begin(indices);
get_indices(std::begin(items), std::end(items)
, std::begin(subitems), std::end(subitems)
, std::begin(indices));
for (auto i : indices) {
std::cout << i << ", ";
}
return 0;
}
We can use find_if to simplify the implementation of the function:
template<class SourceIt, class SelectIt, class IndexIt>
void get_indicies(SourceIt begin, SourceIt end, SelectIt sbegin, SelectIt send, IndexIt dest) {
auto scan = begin;
for(; sbegin != send; ++sbegin) {
auto&& key = *sbegin;
scan = std::find_if(scan, end, [&](auto&& obj) { return obj >= key; });
if(scan == end) break;
for(; scan != end && *scan == key; ++scan) {
*dest = std::distance(begin, scan);
++dest;
}
}
}
This doesn't make it that much shorter, but the code looks a little cleaner now. You're scanning until you find something as big as or equal to the key, and then you copy indicies to the destination as long as the source matches key.
maybe I misunderstodd the question. But there is a function in the algorithm library.
std::set_intersection
This does, what you want in one function. See:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
// Input values
std::vector<int> items{ 1,3,6,8,13,17 };
std::vector<int> subitems{ 3,6,17 };
// Result
std::vector<int> result;
// Do the work. One liner
std::set_intersection(items.begin(),items.end(), subitems.begin(),subitems.end(),std::back_inserter(result));
// Debug output: Show result
std::copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
If I misunderstood, then please tell me and I will find another solution.
EDIT:
I indeed misunderstood. You wanted the indices. Then maybe like this?
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using Iter = std::vector<int>::iterator;
int main()
{
// Input values
std::vector<int> items{ 1,3,6,8,13,17 };
std::vector<int> subitems{ 3,6,17 };
// Result
std::vector<int> indices{};
Iter it;
// Do the work.
std::for_each(subitems.begin(), subitems.end(), [&](int i) {it = find(items.begin(), items.end(), i); if (it != items.end()) indices.push_back(std::distance(items.begin(),it));});
// Debug output: Show result
std::copy(indices.begin(), indices.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
Unfortunately a very long "one-liner".
I need to think more . . .
The answer is yes but it will come with C++20:
you can use ranges for this purpose:
first make a view with some predicate you like:
auto result = items | ranges::view::filter(predicate);
then take the iterator to the original array from base, for example result.begin().base() will give you the iterator to the begin of result in the original array.
#include <algorithm>
#include <iostream>
#include <vector>
#include <iterator>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
int main()
{
std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
std::vector<int> subitems = { 3, 6, 17 };
auto predicate = [&](int& n){
for(auto& s : subitems)
if(n == s)
return true;
return false;
};
auto result = items | ranges::view::filter(predicate);
for (auto& n : result)
{
std::cout << n << '\n';
}
for(auto it = result.begin(); it != result.end(); ++it )
std::cout << it.base() - items.begin() << ' ';
}
see the godbolt
By using std::set_intersection, defining an assignment_iterator class and a assignment helper, this is possible:
#include <iterator>
#include <iostream>
#include <algorithm>
#include <vector>
template <typename Transform>
class assignment_iterator
{
Transform transform;
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
assignment_iterator(Transform transform)
: transform(transform)
{}
// For some reason VC++ is assigning the iterator inside of std::copy().
// Not needed for other compilers.
#ifdef _MSC_VER
assignment_iterator& operator=(assignment_iterator const& copy)
{
transform.~Transform();
new (&transform) Transform(copy.transform);
return *this;
}
#endif
template <typename T>
constexpr assignment_iterator& operator=(T& value) {
transform(value);
return *this;
}
constexpr assignment_iterator& operator* ( ) { return *this; }
constexpr assignment_iterator& operator++( ) { return *this; }
constexpr assignment_iterator& operator++(int) { return *this; }
};
template <typename Transform>
assignment_iterator<Transform> assignment(Transform&& transform)
{
return { std::forward<Transform>(transform) };
}
int main()
{
int items[] = { 1, 3, 6, 8, 13, 17 };
int subitems[] = { 3, 6, 17 };
std::vector<int> indices;
std::set_intersection(std::begin(items), std::end(items)
, std::begin(subitems), std::end(subitems)
, assignment([&items, &indices](int& item) {
return indices.push_back(&item - &*std::begin(items));
})
);
std::copy(indices.begin(), indices.end()
, assignment([&indices](int& index) {
std::cout << index;
if (&index != &std::end(indices)[-1])
std::cout << ", ";
})
);
return 0;
}
Demo
It's more code, but maybe assignment is a more generic means to do other operations, that currently require a specific implementations like back_inserter and ostream_iterator, and thus be less code in the long run (e.g. like the other use above with std::copy)?
This should work properly all the time based on the documentation here:
elements will be copied from the first range to the destination range.
You can use std::find and std::distance to find the index of the match, then put it in the container.
#include <vector>
#include <algorithm>
int main ()
{
std::vector<int> v = {1,2,3,4,5,6,7};
std::vector<int> matchIndexes;
std::vector<int>::iterator match = std::find(v.begin(), v.end(), 5);
int index = std::distance(v.begin(), match);
matchIndexes.push_back(index);
return 0;
}
To match multiple elements, you can use std::search in similar fashion.

Step/Stride Iterator for use with std::minmax_element

I have a 1D float array which represents a m *n (rows and columns) table of float values. My requirement is to find a min/max element for each row and column. For rows I can easily do it by using std::minmax_element by specifying a range of n elements. But for columns I need to use a stride iterator as elements are placed are not contiguous but placed at a step interval of n. Is there a standard iterator in boost/STL that can be used. The other option is to write my own version of it.
What is the best course ?
Two ways (among many) of doing it are using range-v3 (or boost::range) and boost::iterator.
With range-v3, it's immediate:
#include <iostream>
#include <range/v3/all.hpp>
using namespace ranges;
int main() {
std::vector<std::size_t> src{1, 2, 3, 4, 5, 6, 7};
const auto min_max = minmax(src | view::stride(3));
std::cout << min_max.first << " " << min_max.second << std::endl;
}
In boost::range, use boost::adaptors::strided.
#include <boost/range/adaptor/strided.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/assign.hpp>
#include <boost/range/algorithm.hpp>
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
using namespace boost::adaptors;
using namespace boost::assign;
std::vector<int> input;
int arr[] = {1, 2, 3, 4, 5, 6, 7};
auto str = std::make_pair(&arr[0], &arr[8]) | strided(3);
std::cout << *boost::range::min_element(str) << " " << *boost::range::max_element(str) << std::endl;
}
Note the following:
A range can be defined by a pair of iterators, so I used that here although a simpler form is possible (it fits your use case of heap-allocated C-style arrays).
Unfortunately, this sub-library doesn't seem to have min-max (or at least I couldn't find it), so there are two calls, one for min, and one for max.
Given a random-access iterator, it's not very difficult to build a strided forward boost::iterator_facade:
#include <iostream>
#include <vector>
#include <iterator>
#include <cstddef>
#include <algorithm>
#include <boost/iterator/iterator_facade.hpp>
template<typename It>
class stride_iterator :
public boost::iterator_facade<
stride_iterator<It>,
typename std::iterator_traits<It>::value_type,
boost::forward_traversal_tag> {
public:
stride_iterator() = default;
stride_iterator(It it, It end_it, std::size_t stride) :
m_it{it}, m_end_it{end_it}, m_stride{stride}
{}
private:
friend class boost::iterator_core_access;
void increment() {
if(std::distance(m_it, m_end_it) < m_stride) {
m_it = m_end_it;
return;
}
std::advance(m_it, m_stride);
}
bool equal(const stride_iterator<It> &other) const {
return m_it == other.m_it;
}
typename std::iterator_traits<It>::value_type &dereference() const {
return *m_it; }
It m_it, m_end_it;
std::size_t m_stride;
};
This should be enough for std::minmax_element. (Adding a bit of logic, the decrement and advance members, and changing the tag, would make it into a random-access iterator too.)
int main() {
using vec_t = std::vector<int>;
vec_t v{1, 2, 3, 4, 5, 6, 7};
stride_iterator<vec_t::iterator> b{std::begin(v), std::end(v), 3}, e{std::end(v), std::end(v), 3};
auto min_max = std::minmax_element(b, e);
std::cout << *min_max.first << " " << *min_max.second << std::endl;
}

Synchronously sort two containers by elements of first of them

Given a two containers: std::list< int > a; and std::list< int > b;, — a.size() == b.size(). Need to sort containers a and b synchronously, i.e. each swap of elements in a should cause a swapping corresponding elements in b (correspondence in sense of positional indices). Assume, that elements in a and b are very heavyweight. I.e. you can't make its copies.
What is the perfect STL-way to do it? How to use std::sort to perform the operation? What to do if a is const?
What I do currently:
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <utility>
#include <iterator>
#include <algorithm>
#include <list>
#include <vector>
#include <cstdlib>
#include <cassert>
template< typename first, typename second >
void
sort_synchronously(first & f, second & s)
{
std::size_t sz = f.size();
assert(sz == s.size());
struct P
{
typename first::iterator pfirst;
typename second::iterator psecond;
bool operator < (P const & p) const { return (*pfirst < *p.pfirst); }
void swap(P & p) noexcept { std::iter_swap(pfirst, p.pfirst); std::swap(pfirst, p.pfirst); std::iter_swap(psecond, p.psecond); std::swap(psecond, p.psecond); }
};
std::vector< P > p;
p.reserve(sz); // O(N) additional memory
auto fi = std::begin(f);
auto si = std::begin(s);
for (std::size_t i = 0; i < sz; ++i) {
p.push_back({fi, si});
++fi;
++si;
}
std::sort(std::begin(p), std::end(p)); // O(N * log N) time
}
int
main()
{
std::list< int > a{5, 4, 3, 2, 1};
std::list< int > b{1, 2, 3, 4, 5};
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
sort_synchronously(a, b);
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
return EXIT_SUCCESS;
}
But I can't provide free swap (based on P::swap) function for struct P. Is it unavoidable limitation of the language (I can't define non-lambda function inside function scope, but can define non-template class)?
ADDITIONAL:
I found that presence the swap free function overloading is not the type requirement for std::sort function. Just MoveConstructible and MoveAssignable are. Therefore the code is more appropriate (but still incomplete). There is the really hard issue: swap of elements in range provided to std::sort is (evidently) splitted into series of consistuent operations: T tmp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(tmp);. Therefore I can't swap (during std::sort) referenced elements of containers itself but only the iterators to them.
One reasonably simple solution is to build a vector v of iterators into your lists, and sort that. Then, the ith element of v points to the elements in the lists that should occupy the ith position in the sorted lists, which you can rebuild. Performance might not be optimal, due to the use of the auxiliary containers, but it's easy to understand.
void ZippedSort(std::list<A>& a, std::list<B>& b) {
using PairOfIts = pair<decltype(a.begin()), decltype(b.begin())>;
vector<PairOfIts> v;
auto i = a.begin();
auto j = b.begin();
for (; i != a.end(); ++i, ++j)
v.push_back(make_pair(i, j));
std::sort(v.begin(), v.end(), [](PairOfIts const& i, PairOfIts const& j) { return *i.first < *j.first; } );
list<A> sortedA;
list<B> sortedB;
for (auto& x : v) {
sortedA.splice(sortedA.end(), a, x.first);
sortedB.splice(sortedB.end(), b, x.second);
}
swap(sortedA, a);
swap(sortedB, b);
}
The perfect STL-way to do it is to fill vector with std::pair and create custom comparator which compares only first element in pair. Then you will have sorted vector of pairs.
The proper way to do it is to create an iterator class with something like std::pair<T1 &, T2 &> as it's value_type. It probably should contain an iterator on each sequence that is to be sorted, and properly propagate operations to them.
In fact, that's exactly what boost::zip_iterator does. I recommend using this with an appropriate comparator; or at least using boost::zip_iterator as an example of how it should work.
OK, done. But it looks like (not too dirty) hack: in T tmp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(tmp); chain of std::swap implementation I make std::sort algorithm to perform only middle operation (both other are no-op):
#include <iostream>
#include <iomanip>
#include <type_traits>
#include <utility>
#include <iterator>
#include <algorithm>
#include <vector>
#include <forward_list>
#include <cstdlib>
#include <cassert>
template< typename first, typename second >
void
sort_synchronously(first & f, second & s)
{
std::size_t sz = static_cast< std::size_t >(std::distance(std::cbegin(f), std::cend(f)));
assert(sz == static_cast< std::size_t >(std::distance(std::cbegin(s), std::cend(s))));
struct P
{
typename first::iterator pfirst;
typename second::iterator psecond;
bool signal;
bool operator < (P const & p) const { return (*pfirst < *p.pfirst); }
P(typename first::iterator pf, typename second::iterator ps)
: pfirst(pf)
, psecond(ps)
, signal(false)
{ ; }
P(P &&) : signal(true) { ; }
void operator = (P && p) { if (!p.signal) { std::iter_swap(pfirst, p.pfirst); std::iter_swap(psecond, p.psecond); } }
};
std::vector< P > p;
p.reserve(sz);
auto fi = std::begin(f);
auto si = std::begin(s);
for (std::size_t i = 0; i < sz; ++i) {
p.emplace_back(fi, si);
++fi;
++si;
}
std::sort(std::begin(p), std::end(p));
}
int
main()
{
std::forward_list< int > a{5, 4, 3, 2, 1};
std::forward_list< int > b{10, 20, 30, 40, 50};
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
sort_synchronously(a, b);
std::cout << std::endl;
std::copy(std::cbegin(a), std::cend(a), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
std::copy(std::cbegin(b), std::cend(b), std::ostream_iterator< int >(std::cout, " ")); std::cout << std::endl;
return EXIT_SUCCESS;
}
I am sure modification for static_assert(std::is_const< first >{}); is evident (just change typename first::iterator to typename first::const_iterator and do std::swap(pfirst, p.pfirst); instead of std::iter_swap(pfirst, p.pfirst);).