I want to get a pointer from vector's element which has Fd property set on desired value. Right now I am using this ugly code:
User* Game::GetUser(int fd)
{
for(auto& v : users)
if (v.Fd == fd)
return &v;
return nullptr;
}
How can I change this code to be more similar to this:
void Game::RemoveUser(int fd)
{
users.erase(remove_if(begin(users), end(users), [fd](User const& u)
{
return u.Fd == fd;
}), end(users));
}
Or maybe there are other, much better ways to code this?
How can I change this code to be more similar to this
Just use std::find_if with the same predicate:
User* Game::GetUser(int fd)
{
auto it = find_if(begin(users), end(users), [fd](User const& u)
{
return u.Fd == fd;
}));
return it == end(users) ? nullptr : &*it;
}
to avoid code duplication you can use result of find_if for erase as well:
users_type::iterator findUser( int fd)
{
return find_if(begin(users), end(users), [fd](User const& u)
{
return u.Fd == fd;
}));
}
User* Game::GetUser(int fd)
{
auto it = findUser( fd );
return it == end( users ) ? nullptr : &*it;
}
void Game::RemoveUser(int fd)
{
auto it = findUser( fd );
if( it != end(users) )
users.erase( it );
}
you may consider to use different container if such operations has to happen quite often, as both std::find_if and std::remove_if are linear to number of elements. Or you may want to keep users sorted and use std::equal_range() instead of std::find_if() which is log(N), but you would need to use findUser to find proper location to insert or use std::sort after insertion(s).
Related
I wrote a small function whose aim is to go through a list of elements in order, do some checks on the hashmap value of it and if they pass, return it.
QString ElementContainer::getPreferedAvailableElement() const
{
QStringList preferred_priority = { A, B, C, D, E };
foreach(QString element, preferred_priority){
Element* data = m_hashmap.value(element, nullptr);
if(data && data->isReady()){
return element;
}
}
return QString("");
}
I know that those functional kind of std functions should not be forced no matter if it makes sense or not. I am just curious how you can transform this and if it maybe is more verbose.
You can use std::find_if like this:
QString ElementContainer::getPreferedAvailableElement() const
{
QStringList preferred_priority = { A, B, C, D, E };
auto it = std::find_if(preferred_priority.begin(),
preferred_priority.end(),
[this](const QString & element)
{
Element* data = m_hashmap.value(element, nullptr);
return (data && data->isReady());
});
if (it != preferred_priority.end())
{
return *it;
}
return QString("");
}
I have some operation that I would like to use with std::accumulate, but it may fail for some elements, in which case the accumulation should be aborted. With exceptions, I could throw an exception in case of failure, but I need to build without exceptions. With exceptions, this would look like this (the operation being greatly simplified):
std::optional<int> sum_accumulate_with_exceptions(
std::vector<int> const& aVec) {
try {
return std::accumulate(aVec.begin(), aVec.end(), 0,
[](int oldSum, int current) {
if (current > 42)
throw std::logic_error{"too large"};
return oldSum + current;
});
} catch (std::logic_error const&) {
return std::nullopt;
}
}
Actually, even with the possibility of using exceptions, this appears quite wasteful, as I am not interested in the particular exception thrown, and so the overhead of exceptions is unnecessarily large.
Using std::accumulate, I could use an error flag like this:
std::optional<int> sum_accumulate_without_exceptions(
std::vector<int> const& aVec) {
bool errored = false;
int res = std::accumulate(aVec.begin(), aVec.end(), 0,
[&errored](int oldSum, int current) {
if (errored) return 0;
if (current > 42) {
errored = true;
return 0;
}
return oldSum + current;
});
return errored ? std::optional<int>{} : res;
}
However, this is clearly bad, since this always iterates over the whole container, which might be large.
I came up with my own variant of std::accumulate:
template <typename It, typename T, typename Op>
std::optional<T> accumulate_shortcircuit(It aBegin, It aEnd, T aInit,
const Op& aOp) {
std::optional<T> res = std::move(aInit);
for (auto it = aBegin; it != aEnd; ++it) {
res = aOp(*res, *it);
if (!res) break;
}
return res;
}
This can be used nicely for the example case like this:
std::optional<int> sum_accumulate_shortcircuit(std::vector<int> const& aVec) {
return accumulate_shortcircuit(aVec.begin(), aVec.end(), 0,
[](int oldSum, int current) {
if (current > 42) {
return std::optional<int>{};
}
return std::optional<int>{oldSum + current};
});
}
However, I would prefer using std::accumulate (or any other standard library algorithm [edit:] or combination of them) itself, instead of using a replacement. Is there any way to achieve this?
While I was using C++17's std::optional in the example, ideally this would only use C++14 standard library algorithms, but I am also interested in solutions from newer/future standard versions.
[edit:] Based on #NathanOliver's answer, accumulate_shortcircuit could be implemented like this without having the range TS:
template <typename It, typename T, typename Op>
std::optional<T> accumulate_shortcircuit(It aBegin, It aEnd, T aInit,
const Op& aOp) {
std::optional<T> res = std::move(aInit);
std::all_of(aBegin, aEnd, [&](const T& element) {
return static_cast<bool>(res = aOp(*res, element));
});
return res;
}
You need an algorithm that has short circuiting built in. The first one that comes to to mind is std::any_of. You can use a lambda to do the sumation, and then return true to it once you've reached the point where you want to return. That would give you a function like
int sum_accumulate_shortcircuit(std::vector<int> const& aVec)
{
int sum = 0;
std::any_of(aVec.begin(), aVec.end(),
[&](auto elm) { if (elm > 42) return true; sum += elm; return false; });
return sum;
}
For future reference, this type of composition of algorithms/operations will be much easier in C++20 (with the inclusion of the ranges TS). This is an example from the current TS using accumulate and view::take_while:
auto sum = ranges::accumulate(my_vec | view::take_while([] (auto i) -> i <= 42), 0);
I need to implement the following datastructure for my project. I have a relation of
const MyClass*
to
uint64_t
For every pointer I want to save a counter connected to it, which can be changed over time (in fact only incremented). This would be no problem, I could simply store it in a std::map. The problem is that I need fast access to the pointers which have the highest values.
That is why I came to the conclusion to use a boost::bimap. It is defined is follows for my project:
typedef boost::bimaps::bimap<
boost::bimaps::unordered_set_of< const MyClass* >,
boost::bimaps::multiset_of< uint64_t, std::greater<uint64_t> >
> MyBimap;
MyBimap bimap;
This would work fine, but am I right that I can not modify the uint64_t on pair which were inserted once? The documentation says that multiset_of is constant and therefore I cannot change a value of pair in the bimap.
What can I do? What would be the correct way to change the value of one key in this bimap? Or is there a simpler data structure possible for this problem?
Here's a simple hand-made solution.
Internally it keeps a map to store the counts indexed by object pointer, and a further multi-set of iterators, ordered by descending count of their pointees.
Whenever you modify a count, you must re-index. I have done this piecemeal, but you could do it as a batch update, depending on requirements.
Note that in c++17 there is a proposed splice operation for sets and maps, which would make the re-indexing extremely fast.
#include <map>
#include <set>
#include <vector>
struct MyClass { };
struct store
{
std::uint64_t add_value(MyClass* p, std::uint64_t count = 0)
{
add_index(_map.emplace(p, count).first);
return count;
}
std::uint64_t increment(MyClass* p)
{
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return add_value(p, 1);
}
else {
remove_index(it);
++it->second;
add_index(it);
return it->second;
}
}
std::uint64_t query(MyClass* p) const {
auto it = _map.find(p);
if (it == std::end(_map)) {
// in this case, we'll create one - we could throw instead
return 0;
}
else {
return it->second;
}
}
std::vector<std::pair<MyClass*, std::uint64_t>> top_n(std::size_t n)
{
std::vector<std::pair<MyClass*, std::uint64_t>> result;
result.reserve(n);
for (auto idx = _value_index.begin(), idx_end = _value_index.end() ;
n && idx != idx_end ;
++idx, --n) {
result.emplace_back((*idx)->first, (*idx)->second);
}
return result;
}
private:
using map_type = std::map<MyClass*, std::uint64_t>;
struct by_count
{
bool operator()(map_type::const_iterator l, map_type::const_iterator r) const {
// note: greater than orders by descending count
return l->second > r->second;
}
};
using value_index_type = std::multiset<map_type::iterator, by_count>;
void add_index(map_type::iterator iter)
{
_value_index.emplace(iter->second, iter);
}
void remove_index(map_type::iterator iter)
{
for(auto range = _value_index.equal_range(iter);
range.first != range.second;
++range.first)
{
if (*range.first == iter) {
_value_index.erase(range.first);
return;
}
}
}
map_type _map;
value_index_type _value_index;
};
Ticket* Championship::findTicketByFanID(unsigned int id) {
hTabTickets::iterator it = tickets.begin();
while(it != tickets.end()) {
if(it->getOwnerID() == id)
return it;
}
}
Hello there
I'd like to return the object that is on iterator it. tickets is an hash table that holds objects of the type Ticket. When I search for that, there is any solution to return that object?
I want to be able to do this so I can do Ticket t1 = findTicketByFan(id);
hTabTickets:
struct eqTicket {
bool operator() (const Ticket &b1, const Ticket &b2) const{
return b1.getID() == b2.getID();
}
};
struct hTicket{
int operator() (const Ticket &b1) const{
return b1.getID();
}
};
typedef tr1::unordered_set<Bilhete, hTicket, eqTicket> hTabTickets;
Regards
Maybe you want this:
Ticket* Championship::findTicketByFanID(unsigned int id) {
for (hTabTickets::iterator it = tickets.begin(); it != tickets.end(); ++it) {
if(it->getOwnerID() == id)
return &(*it);
}
return NULL;
}
If you search for an id very frequently, you may want to change that set for a map.
References come to a great use here. Typically returning a local pointer is not a good idea. An example for your case is given below. Two problems with your the code: (a) not all paths return value and (b) hash table should locate quickly (ideally O(1)), so looping is a bad idea. Further, it is not being incremented in the loop.
class Campeonato {
// Using STL hashmap
map<unsigned int, int> tickets;
public:
// the return value tells you whether to use iterator or not.
bool findTicketByFanID(unsigned int id, map<unsigned int, int>::iterator &it) {
// std::map provides method find
it = tickets.find(id);
if (it != tickets.end())
return true;
return false;
}
};
I want to be able to do this so I can do Ticket t1 = findTicketByFan(id);
If it is your intention, return type is not correct, it should return by value or (const) reference, not pointer. Another issue, you use std::unordered::set and try to search in a loop, but you should use std::unordered_set::find() instead:
Ticket Championship::findTicketByFanID(unsigned int id)
{
hTabTickets::iterator it = tickets.find( Ticket( id ) );
if( it == tickets.end() ) // not found do something
throw std::runtime_error( "Ticket not found" );
return *it;
}
if creating temporary ticket is too expensive, you should use std::unordered_map<int,Ticket> instead and use id as a key. Then this function would be:
Ticket Championship::findTicketByFanID(unsigned int id)
{
hTabTickets::iterator it = tickets.find( id );
if( it == tickets.end() ) // not found do something
throw std::runtime_error( "Ticket not found" );
return it->second;
}
hTabTickets::iterator it = tickets.begin()
'it' variable's type neither a Ticket, neither a Ticket*, it's a pair which contains key and value variables, you are using it wrong, probably you need to write:
it->second
to access the Ticket itself
I have to process an std::vector either forwards or in reverse, depending upon a boolean flag. What's the most elegant way to accomplish this? Before needing to do it in reverse I had:
BOOST_FOREACH(const CType &foo, vec) {
...
}
However, now I have the horrendous-looking:
for (int i=undoing ? (vec.size()-1) : 0; undoing ? (i >= 0) : (i < vec.size()); undoing ? (i--) : (i++)) {
const CType &foo = vec[i];
...
}
Is there a better way?
I don't know that people would call it elegant, but there's:
auto do_it = [](const CType& elem)
{
...
};
if (iterate_forward) {
std::for_each(vec.begin(), vec.end(), do_it);
}
else {
std::for_each(vec.rbegin(), vec.rend(), do_it);
}
Add a template function that works with either the forward iterators or reverse iterators. Call the function using the appropriate iterator based on the value of undoing.
template <typename Iterator>
void doStuff(Iterator iter, Iterator end)
{
for ( ; iter != end; ++iter )
{
// Do stuff
}
}
if ( undoing )
{
doStuff(vec.rbegin(), vec.rend());
}
else
{
doStuff(vec.begin(), vec.end());
}
How about keeping the loop running as it is from 0 to vector.size, but reading the array in the direction you need.
int idx;
for (int i =0; i < vec.size(); i ++)
{
if (undoing) // assuming going forward
idx = i;
else // going backwards
idx = vec.size() - i - 1;
const CType &foo = vec[idx];
}
You may also use Boost.Range-based solution. It's similar to the one using STL algorithms, already proposed.
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/algorithm/for_each.hpp>
// In C++11 lambda expression can be used instead
struct my_fun
{
void operator()(const CType& elem) const
{
/*...*/
}
};
/*...*/
using namespace boost::adaptors;
if ( iterate_forward )
boost::for_each(my_vect, my_fun());
else
boost::for_each(my_vect | reversed, my_fun());