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
Related
Boost MultiIndex Container, when defined to have hashed_non_unique keys, can group equivalent keys together and return them all against an equal_range query, as mentioned here. But I see no way of querying the largest range (or n largest ranges) in a set. Without comparing between the range sizes of distinct hashes, which can become computationally very expensive, is there a way to query the largest equal ranges?
If we consider a simple example, such as this one, I would like to query by frequency and get Tom as the first result, and then Jack and Leo in no particular order.
Ok, if you're using non-unique hashed indices, turns out equal_range does not invoke equality comparison for all the elements in the returned range (unlike common implementations of std::unordered_multimap, BTW), so the following can be very efficient:
template<typename HashIndex>
std::multimap<
std::size_t,
std::reference_wrapper<const typename HashIndex::value_type>,
std::greater<std::size_t>
> group_sizes(const HashIndex& i)
{
decltype(group_sizes(i)) res;
for(auto it=i.begin(),end=i.end();it!=end;){
auto next=i.equal_range(*it).second;
res.emplace((std::size_t)std::distance(it,next),*it);
it=next;
}
return res;
}
To check how efficient this actually is, let's try instrumenting the element type:
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <cstring>
#include <functional>
#include <iostream>
#include <string>
#include <tuple>
#include <map>
template<typename HashIndex>
std::multimap<
std::size_t,
std::reference_wrapper<const typename HashIndex::value_type>,
std::greater<std::size_t>
> group_sizes(const HashIndex& i)
{
decltype(group_sizes(i)) res;
for(auto it=i.begin(),end=i.end();it!=end;){
auto next=i.equal_range(*it).second;
res.emplace((std::size_t)std::distance(it,next),*it);
it=next;
}
return res;
}
struct instrumented_string:std::string
{
using std::string::string;
static void reset_nums()
{
num_hashes=0;
num_eqs=0;
}
static std::size_t num_hashes,num_eqs;
};
std::size_t instrumented_string::num_hashes=0;
std::size_t instrumented_string::num_eqs=0;
bool operator==(const instrumented_string& x,const instrumented_string& y)
{
++instrumented_string::num_eqs;
return static_cast<std::string>(x)==y;
}
std::size_t hash_value(const instrumented_string& x)
{
++instrumented_string::num_hashes;
return boost::hash<std::string>{}(x);
}
using namespace boost::multi_index;
using container=multi_index_container<
instrumented_string,
indexed_by<
hashed_non_unique<identity<instrumented_string>>
>
>;
int main()
{
auto values={"Tom","Jack","Leo","Bjarne","Subhamoy"};
container c;
for(auto& v:values){
for(auto i=100*std::strlen(v);i--;)c.insert(v);
}
instrumented_string::reset_nums();
auto gs=group_sizes(c);
for(const auto& g:gs){
std::cout<<g.first<<": "<<g.second.get()<<"\n";
}
std::cout<<"# hashes: "<<instrumented_string::num_hashes<<"\n";
std::cout<<"# eqs: "<<instrumented_string::num_eqs<<"\n";
}
Output
800: Subhamoy
600: Bjarne
400: Jack
300: Tom
300: Leo
# hashes: 5
# eqs: 5
So, for a container with 2,400 elements, invoking group_sizes has resulted in just 5 hash calculations and 5 equality comparisons (plus ~2,400 iterator increments, of course).
If you really want to get rid of hashes, the following can do:
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <map>
template<typename HashIndex>
struct internal_reference
{
const HashIndex& i;
const typename HashIndex::value_type& r;
std::size_t buc;
};
template<typename HashIndex>
struct internal_reference_equal_to
{
bool operator()(
const typename HashIndex::value_type& x,
const internal_reference<HashIndex>& y)const
{
return
std::addressof(x)==std::addressof(y.r)||
y.i.key_eq()(y.i.key_extractor()(x),y.i.key_extractor()(y.r));
}
bool operator()(
const internal_reference<HashIndex>& x,
const typename HashIndex::value_type& y)const
{
return (*this)(y,x);
}
};
template<typename HashIndex>
struct internal_reference_hash
{
std::size_t operator()(const internal_reference<HashIndex>& x)const
{
return x.buc;
}
};
template<typename HashIndex>
std::multimap<
std::size_t,
std::reference_wrapper<const typename HashIndex::value_type>,
std::greater<std::size_t>
> group_sizes(const HashIndex& i)
{
decltype(group_sizes(i)) res;
for(std::size_t buc=0,buc_count=i.bucket_count();buc<buc_count;++buc){
for(auto it=i.begin(buc),end=i.end(buc);it!=end;){
auto p=i.equal_range(
internal_reference<HashIndex>{i,*it,buc},
internal_reference_hash<HashIndex>{},
internal_reference_equal_to<HashIndex>{});
std::size_t dist=0;
auto next=it;
while(p.first!=p.second){
++p.first;
++dist;
++next;
}
res.emplace(dist,*it);
it=next;
}
}
return res;
}
struct instrumented_string:std::string
{
using std::string::string;
static void reset_nums()
{
num_hashes=0;
num_eqs=0;
}
static std::size_t num_hashes,num_eqs;
};
std::size_t instrumented_string::num_hashes=0;
std::size_t instrumented_string::num_eqs=0;
bool operator==(const instrumented_string& x,const instrumented_string& y)
{
++instrumented_string::num_eqs;
return static_cast<std::string>(x)==y;
}
std::size_t hash_value(const instrumented_string& x)
{
++instrumented_string::num_hashes;
return boost::hash<std::string>{}(x);
}
using namespace boost::multi_index;
using container=multi_index_container<
instrumented_string,
indexed_by<
hashed_non_unique<identity<instrumented_string>>
>
>;
int main()
{
auto values={"Tom","Jack","Leo","Bjarne","Subhamoy"};
container c;
for(auto& v:values){
for(auto i=100*std::strlen(v);i--;)c.insert(v);
}
instrumented_string::reset_nums();
auto gs=group_sizes(c);
for(const auto& g:gs){
std::cout<<g.first<<": "<<g.second.get()<<"\n";
}
std::cout<<"# hashes: "<<instrumented_string::num_hashes<<"\n";
std::cout<<"# eqs: "<<instrumented_string::num_eqs<<"\n";
}
Output
800: Subhamoy
600: Bjarne
400: Jack
300: Tom
300: Leo
# hashes: 0
# eqs: 0
But please bear in mind this version of group_sizes exploits the undocumented fact that elements with hash value h get placed in the bucket h%bucket_count() (or, put another way, internal_reference<HashIndex> hashing is technically not a conformant compatible extension of the index hash function).
It seems like you might be metter served with a std::map<K, std::vector<V> > like interface here.
You would still always have to do the counting.
To have the counting done "magically" you might consider making the "bucket key" a refcounting type.
This would be more magical than I'd be comfortable with for my code-bases. In particular, copied elements could easily cause overcounting.
Approach 1: BMI + RangeV3 for syntactic sugar
Warning: I consider this "advanced", as in the learning curve might be steepish. However, when you wield Ranges with ease, this can become a great productivity boost.
Note also, this does not in any way promise to increase performance. But you should note that no elements are copied, the vector (groups) merely contains subranges, which are iterator ranges into the multi-index container.
Live On Compiler Explorer
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
#include <iomanip>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>
#include <fmt/ostream.h>
namespace bmi = boost::multi_index;
namespace vw = ranges::views;
namespace act = ranges::actions;
struct Person {
int m_id;
std::string m_name;
friend std::ostream& operator<<(std::ostream& os, Person const& p) {
return os << "[" << p.m_id << ", " << std::quoted(p.m_name) << "]";
}
};
typedef bmi::multi_index_container<
Person,
bmi::indexed_by<
bmi::ordered_unique<bmi::member<Person, int, &Person::m_id>>,
bmi::ordered_unique<
bmi::tag<struct by_name_id>,
bmi::composite_key<Person,
bmi::member<Person, std::string, &Person::m_name>,
bmi::member<Person, int, &Person::m_id>>
>
> >
Roster;
template <typename Index, typename KeyExtractor>
std::size_t distinct(const Index& i, KeyExtractor key) {
std::size_t res = 0;
for (auto it = i.begin(), it_end = i.end(); it != it_end;) {
++res;
it = i.upper_bound(key(*it));
}
return res;
}
int main() {
Roster const r {
{1, "Tom"},
{2, "Jack"},
{3, "Tom"},
{4, "Leo"}
};
fmt::print("Roster: {}\n", r);
static constexpr auto eq_ = std::equal_to<>{};
static constexpr auto name_ = std::mem_fn(&Person::m_name);
static constexpr auto size_ = [](auto const& r) constexpr { return std::distance(begin(r), end(r)); };
auto& idx = r.get<by_name_id>();
fmt::print("Distinct: {}, Index: {}\n", distinct(idx, name_), idx);
auto by_name_ = vw::group_by([](auto const&... arg) { return eq_(name_(arg)...); });
auto by_size_ = [](auto const&... subrange) { return (size_(subrange) > ...); };
auto groups = idx | by_name_ | ranges::to_vector;
for (auto&& x : groups |= act::sort(by_size_)) {
fmt::print("#{} persons in group {}: {}\n",
size_(x),
name_(ranges::front(x)),
x);
}
}
Prints:
Roster: {[1, "Tom"], [2, "Jack"], [3, "Tom"], [4, "Leo"]}
Distinct: 3, Index: {[2, "Jack"], [4, "Leo"], [1, "Tom"], [3, "Tom"]}
#2 persons in group Tom: {[1, "Tom"], [3, "Tom"]}
#1 persons in group Jack: {[2, "Jack"]}
#1 persons in group Leo: {[4, "Leo"]}
Note, I merely kept the distinct() function from the original link. You could drop it to remove some noise.
Approach 2: The same, but w/o Boost
Multi-index seems to be supplying nothing more than the ordered container now, so let's simplify:
Live On Compiler Explorer
#include <set>
#include <iostream>
#include <iomanip>
#include <range/v3/all.hpp>
#include <fmt/ranges.h>
#include <fmt/ostream.h>
namespace vw = ranges::views;
namespace act = ranges::actions;
struct Person {
int m_id;
std::string m_name;
friend std::ostream& operator<<(std::ostream& os, Person const& p) {
return os << "[" << p.m_id << ", " << std::quoted(p.m_name) << "]";
}
bool operator<(Person const& o) const { return m_name < o.m_name; }
};
int main() {
std::multiset<Person> const r {
{1, "Tom"},
{2, "Jack"},
{3, "Tom"},
{4, "Leo"}
};
fmt::print("Roster: {}\n", r);
static constexpr auto eq_ = std::equal_to<>{};
static constexpr auto name_ = std::mem_fn(&Person::m_name);
static constexpr auto size_ = [](auto const& r) constexpr { return std::distance(begin(r), end(r)); };
auto by_name_ = vw::group_by([](auto const&... arg) { return eq_(name_(arg)...); });
auto by_size_ = [](auto const&... subrange) { return (size_(subrange) > ...); };
auto groups = r | by_name_ | ranges::to_vector;
for (auto&& x : groups |= act::sort(by_size_)) {
fmt::print("#{} persons in group {}: {}\n",
size_(x),
name_(ranges::front(x)),
x);
}
}
Prints
Roster: {[2, "Jack"], [4, "Leo"], [1, "Tom"], [3, "Tom"]}
#2 persons in group Tom: {[1, "Tom"], [3, "Tom"]}
#1 persons in group Jack: {[2, "Jack"]}
#1 persons in group Leo: {[4, "Leo"]}
Bonus: Slightly more simplified assuming equality operator on Person suffices: https://godbolt.org/z/58xsTK
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.
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;
}
I am keeping reading Stanford tutorials and currently solving tasks on STL. The task is to write a function which accepts a map with names of films and their rankings. This function should return a set container with top 3 films according to critics review. Here is my solution:
#include <iostream>
#include <set>
#include <map>
#include <numeric>
#include <iterator>
#include <string>
using namespace std;
struct compare {
bool operator() (const double& a, const double& b) const {
return (a > b);
}
};
set <string> list(map <double, string, compare>& films) {
set <string> critics;
map<double, string, compare> ::iterator it = films.begin();
if (films.size() <= 3) {
critics.insert(films.begin()->second, films.end()->second);
}
else {
for (int i = 0; i < 3; ++i, ++it){
critics.insert(it->second);
}
};
return critics;
}
int main() {
map <double, string, compare> films;
films[5.0] = "a";
films[8.0] = "b";
films[10.0] = "c";
films[7.4] = "d";
set <string> critics = list(films);
copy(critics.begin(), critics.end(), ostream_iterator <string>(cout, " "));
cin.get();
}
Unfortunately, it keeps on throwing out an error:
error C2675: unary '++' : 'std::string' does not define this operator or a conversion to a type acceptable to the predefined operator
I have read MSDN documentation on the error but as I am new to this could not understand the meaning of the problem. Could you, please, hint me at that?
This statement
critics.insert(films.begin()->second, films.end()->second);
is invalid. The compiler consideres arguments films.begin()->second and films.end()->second of type std::string as a pair of iterators and tries to apply operator ++. Of course this results in an error.
You should use standard algorithm std::transform with std::insert_iterator that to copy strings from the map to the set.
Here is a demonstrative program that shows the approach
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <algorithm>
#include <iterator>
#include <functional>
int main()
{
std::map<double, std::string, std::greater<double>> m =
{
{ 2.2, "B" }, { 1.1, "A" }, { 4.4, "D" }, { 5.5, "E" }, { 3.3, "C" }
};
for ( const auto &p : m )
{
std::cout << p.first << '\t' << p.second << std::endl;
}
std::set<std::string> s;
std::transform( m.begin(), std::next( m.begin(), 3 ),
std::inserter( s, s.end() ),
[]( const auto &p ) { return p.second; } );
for ( const auto &t : s ) std::cout << t << ' ';
std::cout << std::endl;
return 0;
}
The program output is
5.5 E
4.4 D
3.3 C
2.2 B
1.1 A
C D E
This question already has answers here:
Why is there no transform_if in the C++ standard library?
(11 answers)
Closed 8 years ago.
Which algorithm or combine of algorithms can use for following situation?
struct Term
{
int ix;
double f;
};
std::vector<Term> terms = <intitalize terms>;
std::vector< int > termIxVector;
// NEED get all `ix` from the `terms` where term.f < 1.0,
// and insert 'ix' result to termIxVector.
//i.e. equavalent this loop:
for(std::size_t i = 0; i < terms.size(); ++i)
if ( terms[i].f < 1.0 )
termIxVector.push_back(terms[i].ix);
std::copy_if copies only Term structure. std::transform - doesn't support predicate.
Use std::for_each with a lambda
std::for_each(terms.begin(), terms.end(),
[&termIxVector](Term const& t) {
if(t.f < 1.0) termIxVector.push_back(t.ix);
});
Or a range based for
for(auto const& t : terms) {
if(t.f < 1.0) termIxVector.push_back(t.ix);
}
These kind of compositions of elementary algorithms are most conveniently done with Boost.Range:
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <iostream>
#include <iterator>
#include <vector>
struct Term
{
int ix;
double f;
};
int main()
{
using namespace boost::adaptors;
auto terms = std::vector<Term>{ {0, 0.1}, {1, 1.1}, {2, 0.8}, {3, 1.2}, {4, 0.9} };
auto termIxVector = terms
| filtered([](auto const& x){ return x.f < 1.0; }) // select
| transformed([](auto const& y){ return y.ix; }) // project
;
boost::copy(termIxVector, std::ostream_iterator<int>(std::cout, ","));
}
Live Example using Clang 3.5 SVN which prints the indices 0, 2, and 4 of your Term elements having a double less than 1.0.
NOTE: these ranges are lazily computed, and termIxVector is not a std::vector<int> actually. If you want that, you need to do something like
std::vector<int> termIxVector;
boost::push_back(termIxVector, /* RHS of the same range composition as above */);
If you really want to use std::copy_if, then an idea is to overload operator int() in the struct and use the standard std::copy_if with a unary predicate (lambda or functor), as this will allow you to convert Term to int. Code below:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
struct Term{
int ix;
double f;
operator int(){
return ix;
}
};
struct predicate{ // if you don't want a lambda
bool operator()(const Term& t){
return (t.f < 1.0 ? true : false);
}
};
int main()
{
std::vector<Term> terms = {{1, 0.1}, {2, 0.2}, {3, 1.1}, {4, 0.9}};
std::vector< int > termIxVector;
std::copy_if(terms.begin(), terms.end(),
back_inserter(termIxVector), predicate());
for(auto& elem: termIxVector)
std::cout << elem << std::endl;
}