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("");
}
Related
so basically i have a vector of objects called "Things" that I populate over the console. These are the name of the object I populate them with (tom, coin, coin, bomb).
for (int i = 0; i < 5; i++){
for (Thing * t : *locations[i]->getThings()) {
if (t->getName().compare("bomb") == 0){
for (Thing * all : *locations[i]->getThings()){
if(all->getName().compare("tom") != 0){
locations[i]->remove(all);
}
}
}
}
}
This code is run every time to check if there is a "thing" object called bomb in the list and would remove every other object other than tom.
So from the populated example above, the expected list should just be {tom}. However when the code runs it is {tom, coin} which means it fails to delete the other "non-tom" object
The problem is that you are modify the Things vector while you are still iterating through it, so you are going to be invalidating the iterators that the for loops are using, which is undefined behavior.
You can do something more like this instead, using the Erase-Remove idiom:
const auto is_bomb = [](const Thing *t){ return t->getName() == "bomb"; };
const auto is_not_tom = [](const Thing *t){ return t->getName() != "tom"; };
...
auto *things = locations[i]->getThings();
if (std::any_of(things->begin(), things->end(), is_bomb)){
things.erase(
std::remove_if(things->begin(), things->end(), is_not_tom),
things->end()
);
}
Or, if you are using C++20 or later:
const auto is_bomb = [](const Thing *t){ return t->getName() == "bomb"; };
const auto is_not_tom = [](const Thing *t){ return t->getName() != "tom"; };
...
auto *things = locations[i]->getThings();
if (std::any_of(things->begin(), things->end(), is_bomb)){
std::erase_if(*things, is_not_tom);
}
I'm a beginner to the STL and I used it to make a simple hangman project. Full code here: https://github.com/SamtheSaint/Hangman.
I needed to detect multiple occurrences of letters in a vector but I could not and ended up working around it to finish the program. Is there a simpler way to do this?
iter = find(gameWord.begin(), gameWord.end(), playGuess);
if (iter == gameWord.end()) {
guesses--;
}
while (iter != gameWord.end()) {
iter = find(gameWord.begin(), gameWord.end(), playGuess);
if (iter != gameWord.end()) {
int index = distance(gameWord.begin(), iter);
hiddenWord[index] = playGuess;
*iter = '0'; // so program can find the next one
}
}
I end up destroying gameWord vector so I have to make a copy(which I call checkWord) at the beginning of the loop it's in so I can compare it later to hiddenWord.
You do not need std::map.
You just need two std::string (one which is expression to guess, the other one is the pattern shown to the player) which will be kept in synchronization. This mean you should enclose them in class.
Do not make thing more complicated then it is necessary.
This is quite simple:
class Hangman {
public:
constexpr SecretChar = '_';
Hangman(const std::string& secret)
: mSecret(secret)
{
provideMask();
}
bool guess(char ch) {
auto index = mSecret.find(ch);
if (index == std::string::npos) return false;
if (already_guessed(index)) return false;
updateMaskWith(ch);
return true;
}
std::string mask() const {
return mMask;
}
private:
void provideMask() {
mask = mSecret;
std::replace_if(mMask.begin(), mMask.end(), std::isalpha, SecretChar);
}
bool already_guessed(int index) {
return mMask[index] != SecretChar;
}
void updateMaskWith(char ch) {
auto index = mSecret.find(ch);
while (index == std::string::npos) {
mMask[index] = ch;
index = mSecret.find(ch, index + 1);
}
}
private:
std::string mSecret;
std::string mMask;
};
Now write seperate code which will use this and keep score and you are almost done.
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).
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