I'm trying to create a pricing system for computers and I want to create two vectors, one stores item names, and the other one, prices for the item. My plan is to have two methods, "find_item",that finds the item name inside of a vector and returns its index, and "get_itemPrice", that takes the index from find_item and gets the price. My problem is coming up with a code that takes a string object inside of a vector and returns its index position.
You can simply use std::find. It will return an iterator to the first element equal to the one you are searching for, or to the end() if none is found. You can then use std::distance to get an index from that, if you really need it.
Like this:
vector<int> vect;
int i = 0;
for (vector<int>::iterator iter = vect.begin(); iter != vect.end(); ++iter)
{
if (*iter == vect)
break;
i++;
}
if (i == vect.size())
// not found
else
// i is your index
if you really would like to make an algorithm do it like this, but generally one should not try to beat those in the stl see the algorithm header
unsigned find_str( const vector<string>& vec, const string& key ){
unsigned count = 0;
vector<string>::const_iterator it;
for ( it = vec.begin() ; it < vec.end(); it++ ){
if( *it == key)
return count;
count++;
}
/*throw not found error here.*/
}
Related
I have a std::vector<std::string> textLines that contains a large number of, say city names. I remove the duplicates with:
using namespace std;
vector<string>::iterator iter;
sort(textLines.begin(), textLines.end());
iter = unique(textLines.begin(), textLines.end());
At this point the duplicate elements are all null (empty) strings at the end of the vector with the same size as before unique().
I remove them with:
textLines.resize(distance(textLines.begin(), iter));
This works OK but is there a way to keep the removed duplicates? It would be better (for me) if the duplicates were just moved to the end and not replaced by empty strings.
The new end is pointed out by iter, returned from unique() so there is no problem finding the new end of the vector.
Put another way, I want to know which lines had duplicates and which had not.
You can do this very simply without actually changing your logic drastically.
You can store duplicates in another container, that is captured by the comparison predicate passed to unique():
vector<string> duplicates;
auto iter = unique(textLines.begin(), textLines.end(), [&duplicates](auto& first, auto& second) -> bool {
if (first == second)
{
duplicates.push_back(second);
return true;
}
return false;
});
Live example: here.
With this solution you need additional memory to store elements count.
vector<string>::iterator iter;
vector<string> v{ "a", "b", "a", "t", "a", "g", "t" };
sort(v.begin(), v.end());
// Find number of distinct elements
int count = 1;
auto current = v.cbegin();
for (auto i = v.cbegin() + 1; i < v.cend(); ++i) {
if (*i != *current) {
++count;
current = i;
}
}
// Count every entry
vector<int> vCount(count);
auto currentCount = vCount.begin();
++*currentCount;
for (size_t i = 1; i < v.size(); ++i) {
if (v[i] == v[i-1]) ++*currentCount;
else *++currentCount = 1;
}
iter = unique(v.begin(), v.end());
You could always write your own function, which is advisable for cases like yours where you have a specific request. Something like:
//Define a "bool has(const vector &v, int element)" function before
vector<string> nonDuplicates;
vector<string> duplicates;
for (auto i : textList) {
if (has(nonDupicates, i)) {
duplicates.push(i);
}
else {
nonDuplicates.push(i);
}
}
This is not a very elegant, or fast, way to do it, so you can probably find a better way, but if you do do it this way, use a binary search for has(), if you have sorted it
I am trying to scroll a two dimensional list with an iterator and I know that I'm missing something but I don't know what.
So the idea is that I'll have some commands to parse.
I put them on a list then I want to check if a member of the list is equal to "data.txt" for example. So I did an iterator for this, but as it is a two dimensional list with an std::pair inside it, I don't know how to implement this iterator. I did this but it isn't good, I can't read both list.
typedef std::list<std::string> listStr;
std::list <std::pair<listStr, int> > _execCmd;
int Parser::execCmd()
{
std::list<std::string>::const_iterator i;
for (i = _execCmd.front().first.begin(); i != _execCmd.back().first.end(); ++i)
{
if (*i == "Search.txt")
execSearch();
else if (*i == "data.txt")
execData();
}
return (0);
}
In that case, I stay on the first list "File.txt data.txt contact.txt" (cf: schema) and I can go through the second list "Search.txt employe.csv".
I've tried this too:
int Parser::execCmd()
{
std::list<std::pair<listStr, int> >::const_iterator i;
for (i = _execCmd.begin(); i != _execCmd.end(); ++i)
{
if (*i == "Search.txt")
execSearch();
else if (*i == "data.txt")
execData();
}
return (0);
}
But I can't compile this because I don't know how to compare the iterator with a string (*i == "help")
Somebody can help me please ?
A std::pair<X,Y> contains two members, first which obtains the member of type X and second which obtains the member of type Y.
In your case, thanks to the typedef you have a std::list<std::pair<std::list<std::string>, int> >.
So to iterate over all std::strings in that structure, you need to iterate over the outer list to get the pairs, obtain the first member from each of those (of type std::list<string>, and iterate over ever element of that inner list.
int Parser::execCmd()
{
std::list<std::pair<listStr, int> >::const_iterator i;
for (i = _execCmd.begin(); i != _execCmd.end(); ++i)
{
// i->first is of type std::list<std:string>
for (j = i->first.begin(); j != i->first.end(); ++j)
{
if (*j == "Search.txt")
execSearch();
else if (*j == "data.txt")
execData();
}
}
return (0);
}
In C++11, it is simpler, but still necessary to nest loops.
int Parser::execCmd()
{
std::list<std::pair<listStr, int> >::const_iterator i;
for (const auto &i : _execCmd))
{
// i.first is of type std::list<std:string>
for (const auto &j : i.first)
{
if (j == "Search.txt")
execSearch();
else if (j == "data.txt")
execData();
}
}
return (0);
}
As I said in the comments, the way to iterate over an std::list in c++ is to use the foreach syntax.
The idea of an iterator is to give you a pointer-like access to elements in the container and to provide a way for the container to operate on these elements. For example, given an iterator you can delete an element in a list. Or you could insert an element at specific position.
What you need, however, is just to traverse over the list elements and check whether there is "search.txt" or "data.txt". So you don't need any iterators, you need just the elements. This is what the range-based for loop in c++ is. (Look at this great question: What is the correct way of using C++11's range-based for?)
Note that internally the range-based for loop internally may use iterators.
std::list<std::pair<listStr, int> >::const_iterator i;
for (std::pair<listStr, int> &elementFromOuterList: _execCmd) {
// now given that elementFromOuterList you can do whatever you need to
}
I have an abstract class called Object and I am using std::unordered_map<int, Object*> objects to contain these Objects within a class called DataSet. Each object has an id associated with it.
Normally, when deleting an object from my unordered_map, I can just do iterator = find(id) and then call erase on that iterator.
This is easy and efficient. The problem is, I have to implement a method to delete an entry/pair by value, rather then by the key (which was my id). This gives me the following prototype:
int DataSet::DeleteObject(Object* object)
What is the most efficient way of accomplishing this though? I'm thinking I can do something like this:
if(object){
for(auto kv : objects) {
if(kv.second == object) {
objects.erase(kv);
}
}
return 1;
}
But it seems very inefficient. So what would be the most efficient way to accomplish this?
Don't perform the lookup twice; erase via iterator:
for (auto it = m.begin(); it != m.end(); )
{
if (it->second == needle) { m.erase(it++); }
else { ++it; }
}
This deletes all occurrences of needle. If you want to erase at most the first occurrence, a simpler loop will do:
for (auto it = m.begin(); it != m.end(); ++it)
{
if (it->second == needle) { m.erase(it); break; }
}
If you want to erase exactly one element, you need to add a check that you found any needles. This can be achieved with find_if, which may also be used as a variation of the previous algorithm:
auto it = std::find_if(m.begin(), m.end(),
[&needle](const auto & p) { return p.second == needle; });
if (it != m.end()) { m.erase(it); }
else { /* no such element! */ }
Given a vector such as this:
struct product {
float price;
float shipping;
};
vector<product> products;
how can I remove all the products from the vector apart from the one with the largest shipping to price ratio?
I tried keeping an iterator to the highest one found so far...
vector<product>::iterator it = products.begin();
vector<product>::iterator largest = products.begin();
while (it != products.end())
{
if (it->shipping / it->price > largest->shipping / largest->price)
{
products.erase(largest);
largest = it;
++it;
}
else
{
it = products.erase(it);
}
}
This is all well and good but it fails if the first element in the vector has the highest ratio (it gets deleted). I could get around the problem (I think) if largest was uninitialized and then checking for that in the if statement, but there is no real way of doing this from what I can tell (How to check if the iterator is initialized?).
Any suggestions?
vector<product> products;
//populate products
products.erase(
products.begin(),
std::max_element(
product.begin(),
producted.end()
)
);
products.resize(1u);
This assume you have a suitable operator< for your type, if not, make a comparison function and provide it as the third param to max_element.
EDIT:
This work also, in this case, instead of explicitly find the element and delete the element either side, it will sort to find 1 elemnt, then we can do one erase.
vector<product> products;
//populate products
std::nth_element(
products.begin(),
products.begin()+1,
products.end(),
std::greater<product>()
);
products.resize(1u);
Just rewrite the code to only make one single deletion:
std::vector<product>::iterator largest = products.begin();
for (std::vector<product>::iterator it = products.begin(); it != products.end(); ++it)
{
if (...) { largest = it; }
}
products.erase(it);
You could define largest as the first element and start iteration from the second element, just as followings:
bool operator < (const struct product& p1, const struct product& p2)
{
return p1.price/p1.shipping < p2.price/p2.shipping;
}
vector<product>::iterator largest = products.begin();
vector<product>::iterator it = products.begin();
++it;
while (it != products.end())
{
if (*largest < *it)
{
products.erase(largest);
largest = it;
++it;
}
else
{
it = products.erase(it);
}
}
But there is a bug here, after products.erase(largest) is called, it will be invalidated, so you'd better take the approach other suggested here.
In code below:
map<string,vector<int>> create(ifstream& in, const vector<string>& vec)
{
/*holds string and line numbers into which each string appears*/
typedef map<string,vector<int>> myMap;
typedef vector<string>::const_iterator const_iter;
myMap result;
string tmp;
unsigned int lineCounter = 0;
while(std::getline(in,tmp))
{
const_iter beg = vec.begin();
const_iter end = vec.end();
while (beg < end)
{
if ( tmp.find(*beg) != string::npos)
{
result[*beg].push_back(lineCounter);//THIS IS THE LINE I'M ASKING FOR
}
++beg;
}
++lineCounter;
}
return result;
}
How should I do it (check line commented in code) if I want to use insert method of map instead of using operator[]?
Thank you.
Seriously, I would not do it.
You are only going to complicate your code unnecessarily. You would need a call to the insert to generate the new element in the map and then modify it.
Just for the sake of it (avoiding the double lookup, but building an unnecessary empty vector):
result.insert( std::make_pair( *beg, std::vector<int>() ) )
.first->second.push_back( lineCounter );
EDIT: Real equivalent (functionality and performance):
std::map<std::string,std::vector<int> >::iterator it = result.upper_bound( *beg );
if ( it->first != *beg ) {
it = result.insert( it, std::make_pair( *beg, std::vector<int>() ) ).first;
}
it->second.push_back( lineCounter );
result.insert(pair<string,vector<int>>(*beg,100), vector<int>());
result[*beg].push_back(lineCounter);
This is more complicated (but slower too :-) than your current code, since that achieves two things with a single statement: (implicitly) inserts an empty array into the map, then adds a new element to the array.
map::insert returns a pair containing an iterator to the element (either the one just inserted or the existing one with that key) and a boolean indicating success or failure. You can then call iter->push_back(lineCounter) to add the new line number to the vector.
...and when you're done with all that, realize that this is exactly what operator[] does for you.