How to convert functionbody into lambda? - c++

I want to convert a function body into a lambda. But I don't know how you can implement this if statement into the lambda. I also dont know what is the best to use for this. Is it better to use std::find_if, std::find or something else.
void Inventory::RemoveFromInventory(std::string item)
{
//--bool to check if an item is in the inventory
bool found = false;
for (std::list<Item>::iterator i = m_Inventory.begin(); i != m_Inventory.end(); i++)
{
if (i->GetName() == item)
{
m_Inventory.erase(i);
found = true;
break;
}
}
if (found == false)
{
std::cout << "item not in inventory!" << std::endl;
}
}
Cann someone help me with this conversion problem?

So you want to search for an item that verifies a condition, check whether you found it, and erase it. That's a use case for std::find_if:
void Inventory::RemoveFromInventory(std::string item)
{
auto const foundAt = std::find_if(
begin(m_Inventory),
end(m_Inventory),
[&](Item const &i) { return i.GetName() == item; }
);
if(foundAt == end(m_Inventory)) {
std::cout << "item not in inventory!\n";
} else {
m_Inventory.erase(foundAt);
}
}

Related

Is there a simple way of refactoring this code?

I have a function that have very similar repeating code. I like to refactor it, but don't want any complex mapping code.
The code basically filter out columns in a table. I made this example simple by having the comparison statement having a simple type, but the real comparison can be more complex.
I am hoping there may be some template or lambda technique that can do this.
vector<MyRecord*>& MyDb::Find(bool* field1, std::string * field2, int* field3)
{
std::vector<MyRecord*>::iterator iter;
filterList_.clear();
std::copy(list_.begin(), list_.end(), back_inserter(filterList_));
if (field1)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field1 != *field1)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
if (field2)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field2 != *field2)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
if (field3)
{
iter = filterList_.begin();
while (iter != filterList_.end())
{
MyRecord* rec = *iter;
if (rec->field3 != *field3)
{
filterList_.erase(iter);
continue;
}
iter++;
}
}
return filterList_;
}
Update: Just in case someone is curious, this is my final code. Thanks again everyone. A lot easy to understand and maintain.
vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3)
{
auto compare = [&](MyRecord* rec) {
bool add = true;
if (field1 && rec->field1 != *field1) {
add = false;
}
if (field2 && rec->field2 != *field2) {
add = false;
}
if (field3 && rec->field3 != *field3) {
add = false;
}
return add;
};
filterList_.clear();
std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_), compare);
return filterList_;
}
you can use std::copy_if (as you already/would do a copy anyway)
vector<MyRecord*>& MyDb::Find(bool* field1, std::string* field2, int* field3){
filterList_.clear();
std::copy_if(list_.begin(), list_.end(), back_inserter(filterList_),[&](MyRecord* rec){
// whatever condition you want.
return field3 && rec->field3 != *field3;
});
return filterList_;
}
Is there a simple way of refactoring this code?
As far as I understood your algorithm/ intention, using std::erase_if (c++20) you can replace the entire while loops as follows (Demo code):
#include <vector> // std::erase_if
std::vector<MyRecord*> // return by copy as filterList_ is local to function scope
Find(bool* field1 = nullptr, std::string* field2 = nullptr, int* field3 = nullptr)
{
std::vector<MyRecord*> filterList_{ list_ }; // copy of original
const auto erased = std::erase_if(filterList_, [=](MyRecord* record) {
return record
&& ((field1 && record->field1 != *field1)
|| (field2 && record->field2 != *field2)
|| (field3 && record->field3 != *field3));
}
);
return filterList_;
}
If no support for C++20, alternatively you can use erase–remove idiom, which is in effect happening under the hood of std::erase_if.

Is there a better way to detect multiple occurrences of an item in my vector?

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.

convert function into find_if lambda

I have some trouble to convert my function into a std::find_if lamnda.
below you can see my function
bool Room::ItemInRoomPresent(std::string & item)
{
bool isPresent = false;
for (std::vector<Item>::iterator i = m_RoomItems.begin(); i !=m_RoomItems.end(); i++)
{
if (i->GetName() == item)
{
item == i->GetName();
isPresent = true;
break;
}
}
return isPresent;
}
Can some one help me?
As noted in the comments, if you aren't interested in the position at which an item is found, just that there is at least one matching item anywhere in the sequence, you can use std::any_of and make this essentially a one-liner:
return std::any_of(m_RoomItems.begin(),
m_RoomItems.end(),
[&](Item const& x) { return x.GetName() == item; });
The passed lambda is identical to Kerrek's version but the return value can be used directly.
Like this:
auto it = std::find_if(m_RoomItems.begin(), m_RoomItems.end(),
[&](const Item& x) { return x.GetName() == item; });
return it != m_RoomItems.end();
(The statement item == i->GetName(); in your if statement has no effect, so I omitted it.)

debug assert vector iterator not dereferncable with find_if

I want to convert my for loop into a find_if lambda but I Always get the same result vector iterator not dereferncable.
void SearchObjectDescription(std::vector<std::string>& input, Player & player)
{
//--local variable
bool found = false;
//std::find_if
std::vector<std::string>::iterator i = std::find_if(input.begin(), input.end(),[&](Player player)
{
if ((player.InInventory((*i)) ) == true)
{
std::cout << (player.GetInventory().ItemByName((*i))).ExamineObject() << std::endl;
return true;
}
else
{
std::cout << "Object not in invetory!" << std::endl;
return false;
}
});
//original for loop
//for (std::vector<std::string>::iterator i = input.begin()+1; i != input.end(); i++)
//{
// if (player.InInventory((*i))== true)
// {
// std::cout << (player.GetInventory().ItemByName((*i))).ExamineObject() << std::endl;
// found = true;
// break;
// }
//}
//if (found ==false)
//{
// std::cout << "Object not in invetory!" << std::endl;
//}
}
Can some one help me please?
You are thinking about the lambda wrongly. The std::find_if function takes, as it's third argument, a lambda that takes an element of the vector, and return if it's the element you searched for.
The purpose of the lambda is to take an element and tell if it's the right one.
Yet, your lambda don't receive an element of your vector of string, but takes a player as parameter. But you obviously don't have a list of player, you have a list of string. Why should the element to check should be a Player?
Instead, capture your player variable and receive the element to check. It would look like this:
void SearchObjectDescription(std::vector<std::string>& input, Player & player)
{
auto i = std::find_if(input.begin(), input.end(),[&](const std::string& item)
{
// item is equal to the current element being checked
// player is captured by reference because of [&]
// if the item `item` is in inventory, return true.
return player.InInventory(item);
});
if (i != input.end()) {
// if it's not equal to the end, *i is the found item.
}
}
Note that in C++14, you can receive auto&& in your lambda, and it will be deduced to string&:
std::find_if(input.begin(), input.end(), [&](auto&& item)
{
// type of item is std::string&
// ...
});
You cannot use the i iterator inside of your lambda, because it is not initialized until after std::find_if() exits. You need to use the input parameter of the lambda instead, which will be a std::string from the vector, not a Player object.
Also, you are not checking the return value of std::find_if() to make sure you have a valid iterator before dereferencing it.
You did not translate your for loop to a lambda-based std::find_if() correctly. Try this instead:
void SearchObjectDescription(std::vector<std::string>& input, Player & player)
{
auto i = std::find_if(input.begin()+1, input.end(),
[&](const std::string &s) { return player.InInventory(s); });
if (i != input.end())
{
std::cout << (player.GetInventory().ItemByName(*i)).ExamineObject() << std::endl;
}
else
{
std::cout << "Object not in inventory!" << std::endl;
}
}

C++ concurrency::concurrent_vector

I am having issues with the following piece of code while using threads.
I read on the Microsoft site that appending to the concurrent_vector does not mess with iterators, so I did not provide and mutex for the duration of the find_if operation.
So the error I am receiving is an "Access violation"
I have 6 threads running concurrently. Should I wrap this in a mutex? Does it need one. I'm fairly new to C++.
std::stringstream key;
key << "SearchString " << ID << ", " << "Options" << ", " << Date;
auto &it = std::find_if(
m_spList.begin(), m_spList.end(),
[&key] (std::unique_ptr<IBaseObject>const &bo){
return bo->ID() == key.str();
}
);
if (it != m_spList.end()) {
while (it != m_spList.end()) {
ReplacePartResult* rpr = dynamic_cast<ReplacePartResult*>(it->get());
if (rpr) {
if (rpr->ReplaceItem) {
replaceBOMID = rpr->BOMID > 0 ? rpr->BOMID : 0;
if (_parentPart) {
_parentPart->TemplateBomID = rpr->BOMID;
_parentPart->Name = rpr->Name;
_parentPart->Description = rpr->Description;
}
}
}
it = std::find_if(
++it, m_spList.end(),
[&key](std::unique_ptr<IBaseObject>const &bo){
return bo->ID() == key.str();
}
);
}
}
Not 100% why, but i re-factored the find_if into a new function and explicitly defined my iterator type and it seems to be behaving. Maybe sening the stringstream into the lambda was the issue?
concurrency::concurrent_vector<std::unique_ptr<IBaseObject>>::iterator IBaseObject_FindKey(concurrency::concurrent_vector<std::unique_ptr<IBaseObject>>& mv, std::string const& _key)
{
return std::find_if(std::begin(mv), std::end(mv), [&_key](std::unique_ptr<IBaseObject>const &bo){return bo->ID() == _key; });
}