C++: Map, previous item of a key - c++

I have this map: map<int, int > items.
Given a key, I want that this map returns the item corrisponding to the key if it present, otherwise the map returns the item with key immediately less than the given key.
For example, if I have:
items[0]=0;
items[6]=10;
items[15]=18;
items[20]=22;
than for key=15, I want that the map returns item with value 18, otherwise for key=9, I want that map returns item with value 10.
I haven't find a function for this case. But I tried in this way:
itlow=items.lower_bound(key);
if(!items.count(key))
itlow--;
return itlow->second;
This works as I want, entering in the map a min value items[0]=0 for default, but I know that itlow--; it's not good programming. How can I do? thanks all.

You just need to check if your itlow is already items.begin(). If it is, there's no such element in the map:
itlow=items.lower_bound(key);
if(itlow->first == key)
return itlow->second;
else if(itlow != items.begin())
itlow--;
return itlow->second;
else
throw some_exception();
Instead of throwing exception, you may return iterator, and then you can return items.end() if no such element is found.
#include <iostream>
#include <map>
using namespace std;
map<int, int>::const_iterator find(const map<int, int> &items, int value)
{
auto itlow = items.lower_bound(value);
if(itlow->first == value)
return itlow;
else if(itlow != items.cbegin())
return --itlow;
else
return items.cend();
}
int main()
{
map<int, int> items;
items[2]=0;
items[6]=10;
items[15]=18;
items[20]=22;
auto i = find(items, 0);
if(i != items.cend())
{
cout << i->second << endl;
}
i = find(items, 15);
if(i != items.cend())
{
cout << i->second << endl;
}
i = find(items, 9);
if(i != items.cend())
{
cout << i->second << endl;
}
}

Try this
auto it = prev(map.upper_bound(key));
This works because map.upper_bound returns an iterator pointing to the first element that is greater than key or the past-the-end iterator when such element does not exist. Also, OP explained that map is not empty and key is greater than the first element in the map. If the latter conditions are not met, one should handle separately the case where upper_bound returns map.begin().

Related

Find element in vector of pair and output c++

I have a vector of pair:
typedef pair<string,int> is;
vector<is> v;
I push some values to the vector.
v.push_back(make_pair("One",1));
v.push_back(make_pair("Two",2));
v.push_back(make_pair("Three",3));
v.push_back(make_pair("Four",4));
I need to ask the user to input a name and search the vector , find that name and output it's corresponding int in the pair. If user types "One" I want the input to type 1.
I tried the following.
struct comp_pair_int
{
bool operator()(const pair<string, int>& a, const string& b)
{
return (a.first < b);
}
bool operator()(const string& a, const pair<string, int>& b)
{
return (a < b.first);
}
};
sort(v.begin(),v.end(),comparison);
if (binary_search(v.begin(), v.end(),
"One", comp_pair_int()))
cout << "Element found\n";
else
cout << "Element not found";
That code returns if the element is found or not, but that's not all I want, I also need to output the second element in the pair of the found element. How can I do that?
std::binary_search will only give you a bool result, so it doesn't give sufficient information to get the value of the pair you're looking for.
The idiomatic way to do this is with std::lower_bound, which returns an iterator to the pair you're looking for, like this:
if (auto i = std::lower_bound(v.begin(), v.end(),
"One", comp_pair_int());
i != v.end() && i->first == "One") // lower bound actually found correct pair
cout << "Element found with value " << i->second;
else
cout << "Element not found";
Note that as with binary_search the range needs to be sorted by the same predicate you use for the search.
You could use std::find_if if your vector is not sorted by key.
Would look like this:
auto it = std::find_if(v.begin(), v.end(), [&](const is& item) {
return item.first == user_input;
});
if(it != v.end()) {
int my_item = it->second;
}
else {
// key not found
}

loop hangs for iterated std::erase on std::list

I'm trying to remove duplicate combinations of integer vectors stored in a list using a hash table. Iterating over each integer vector in the list, I:
Calculate the hash_value (thash)
See if the hash value is already in the hash table (pids)
If it's in the hash table, erase that vector from the list.
Otherwise, add that value to the hash_table and increment the list
iterator
Print statements seem to confirm my logic, but the loop hangs at the fourth step of iteration. I've commented the it++ and vz.remove(it) that cause the problem and only show the logic in the code below. The code is also available through ideone: https://ideone.com/JLGA0f
#include<iostream>
#include<vector>
#include<list>
#include<cmath>
#include<unordered_set>
using namespace std;
double hash_cz(std::vector<int> &cz, std::vector<double> &lprimes) {
double pid = 0;
for(auto it = cz.begin(); it != cz.end(); it++) {
pid += lprimes[*it];
}
return pid;
}
int main(){
// create list of vectors
std::list<std::vector<int>> vz;
vz.push_back({2,1});
vz.push_back({1,2});
vz.push_back({1,3});
vz.push_back({1,2,3});
vz.push_back({2, 1});
// vector of log of prime numbers
std::vector<double> lprimes {2, 3, 5, 7};
for (auto it = lprimes.begin(); it != lprimes.end(); it++) {
*it = std::log(*it);
}
std::unordered_set<double> pids;
double thash;
for (auto it = vz.begin(); it != vz.end(); ) {
thash = hash_cz(*it, lprimes);
std::cout << thash << std::endl;
// delete element if its already been seen
if (pids.find(thash) != pids.end()) {
std::cout << "already present. should remove from list" << std::endl;
// vz.erase(it);
}
else {
// otherwise add it to hash_table and increment pointer
std::cout << "not present. add to hash. keep in list." << std::endl;
pids.insert(thash);
// it++;
}
it++;
}
for (auto it = vz.begin(); it != vz.end(); it++) {
for (auto j = it -> begin(); j != it -> end(); j++) {
std::cout << *j << ' ';
}
std::cout << std::endl;
}
return 0;
}
Problem is this line of code:
vz.erase(it);
It keeps iterator where it was ie leaves it invalid. It should be either:
vz.erase(it++);
or
it = vz.erase( it );
Note: std::unoredered_set::insert() return value tells you if insert was succesfull or not (if the same value element is there already), you should call it and check result. In your code you do lookup twice:
if (pids.insert(thash).second ) {
// new element added
++it;
} else {
// insertion failed, remove
it = vz.erase( it );
}
As std::list provides remove_if() your code can be simplified:
vz.remove_if( [&pids,&lprimes]( auto &v ) {
return !pids.insert( hash_cz(v, lprimes) ).second );
} );
instead of whole loop.
If the element has already been seen, you erase() the it node and then increment it at the end of the loop: undefined behaviour. Try erase(it++) instead.
If the element has not been seen, you increment it and then do it again at the end of for, yielding UB if it was end() - 1 as it moves past end.

Find value of index in Map Array using string key

In an array map<string, int> bannd such that each key (of type string) holds a number value, like this
+++++++++++++++
key | value
+++++++++++++++
red | 0
blue | 1
orange| 3
etc...
What is the optimal way to return the value of an index using the key?
I already tried using find like this
band1 = band.find("a");
where a is the key value in the map, but it does not seem to be working.
find returns an iterator pointing to the found key-value pair (if any). You have to dereference that iterator to get the actual mapped value:
int band1;
auto it = band.find("a");
if (it != band.end())
band1 = it->second;
else
/* not found ... */;
Note that *it just gives us the std::pair containing the key and mapped value together. To access the mapped value itself we use it->second.
Alternatively, if you know that the key is in the map, you can use at to get the mapped value for that key:
int band1 = band.at("a");
at will throw an out_of_range exception if the element is not found.
Finally, if you want to access the value with key "a" and you want to automatically add that key to the map if it is not already there, you can use the subscript operator []:
int band1 = band["a"]; //warning: inserts {a, 0} into the map if not found!
Write a function, which takes std::map and std::vector of key as argument. And it will return the corresponding values in std::vector
vector<int> valueReturn(map<string,int> data, vector<string> key) {
vector<int> value;
for(const auto& it: key) {
auto search = data.find(it);
if(search != data.end()) {
value.push_back(data[it]);
std::cout << "Found " << search->first << " " << search->second << '\n';
}
else {
value.push_back(-1); // Inserting -1 for not found value, You can insert some other values too. Which is not used as value
std::cout << "Not found\n";
}
}
return value;
}
int band1 = band["a"];
int band2 = band["b"];
int band3 = band["c"];
int band4 = band["d"];

Removing entries of particular key in C++ STL multimap

I have this sample code to insert entries to a multimap. I am trying to delete particular entries of a specified key. But this code goes into infinite loop. Can someone help me with this code?
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
multimap<string, string> names;
string n;
names.insert(pair<string, string>("Z", "F"));
names.insert(pair<string, string>("Z", "A"));
names.insert(pair<string, string>("S", "T"));
names.insert(pair<string, string>("S", "A"));
names.insert(pair<string, string>("S", "J"));
names.insert(pair<string, string>("D", "H"));
names.insert(pair<string, string>("D", "W"));
names.insert(pair<string, string>("D", "R"));
multimap<string, string>::iterator p;
p = names.find("Z");
if(p != names.end()) { // found a name
do {
cout << n << ", " << p->second;
cout << endl;
if (p->second.compare("A") == 0) {
names.erase(p);
p++;
} else {
p++;
}
} while (p != names.upper_bound("Z"));
}
else{
cout << "Name not found.\n";
}
p = names.find("Z");
if(p != names.end()) { // found a name
do {
cout << n << ", " << p->second;
cout << endl;
} while (p != names.upper_bound("Z"));
}
else{
cout << "Name not found.\n";
}
return 0;
}
In the above I am looking up using Key value "Z" and want to delete "A".
multimap::erase invalidates any iterators to the erase elements, so the lines
names.erase(p);
p++;
erases p, thus invalidating it, and then attempt to increment an invalid iterator. You can fix this by copying p to a temporary, incrementing p, and then erasing the temporary iterator.
multimap<string, string>::iterator temp = p;
++p;
names.erase(temp);
Alternatively if you're using C++11 then multimap::erase returns the next iterator in the container
p = names.erase(p);
Edit: the above isn't actually the source of your infinite loop. In the second loop you don't increment p, so it goes forever. However it is still something you should fix as it can cause unpredictable and difficult to track down bugs.
As said by others, advancing an iterator that points to an element that was just erased is not guaranteed to work. What you can do instead is to use the postfix ++ operator to retrieve an iterator to the element that followed the erased one before it was erased:
names.erase(p++);
In C++11, you can alternatively retrieve the return value of erase, which points to the following element (or is end() if there is no more element):
p = names.erase(p);
It has also been said already that your second loop is an infinite loop by definition because it never increments the counter.
However, there is one more thing that should be said: Your method of checking if the last element in a range of elements has been reached is not very efficient: You call upper_bound in every iteration of the loop, which will cause a new O(log(n)) tree search each time, although the iterator returned will always be the same.
You can obviously improve this by running upper_bound before you enter the loop and store the result. But even better, I'd suggest your run the equal_range function once, and then simply iterate through the range it returned:
typedef multimap<string,string>::const_iterator mapit;
std::pair<mapit,mapit> range = names.equal_range("Z");
mapit it = range.first;
while (it != range.second)
if (it->second == "A")
names.erase(it++);
else
++it;
In C++11, the use of auto will make this look even better.

How to find if a given key exists in a C++ std::map

I'm trying to check if a given key is in a map and somewhat can't do it:
typedef map<string,string>::iterator mi;
map<string, string> m;
m.insert(make_pair("f","++--"));
pair<mi,mi> p = m.equal_range("f");//I'm not sure if equal_range does what I want
cout << p.first;//I'm getting error here
so how can I print what is in p?
Use map::find and map::end:
if (m.find("f") == m.end()) {
// not found
} else {
// found
}
To check if a particular key in the map exists, use the count member function in one of the following ways:
m.count(key) > 0
m.count(key) == 1
m.count(key) != 0
The documentation for map::find says: "Another member function, map::count, can be used to just check whether a particular key exists."
The documentation for map::count says: "Because all elements in a map container are unique, the function can only return 1 (if the element is found) or zero (otherwise)."
To retrieve a value from the map via a key that you know to exist, use map::at:
value = m.at(key)
Unlike map::operator[], map::at will not create a new key in the map if the specified key does not exist.
C++20 gives us std::map::contains to do that.
#include <iostream>
#include <string>
#include <map>
int main()
{
std::map<int, std::string> example = {{1, "One"}, {2, "Two"},
{3, "Three"}, {42, "Don\'t Panic!!!"}};
if(example.contains(42)) {
std::cout << "Found\n";
} else {
std::cout << "Not found\n";
}
}
You can use .find():
map<string,string>::iterator i = m.find("f");
if (i == m.end()) { /* Not found */ }
else { /* Found, i->first is f, i->second is ++-- */ }
C++17 simplified this a bit more with an If statement with initializer.
This way you can have your cake and eat it too.
if ( auto it{ m.find( "key" ) }; it != std::end( m ) )
{
// Use `structured binding` to get the key
// and value.
const auto&[ key, value ] { *it };
// Grab either the key or value stored in the pair.
// The key is stored in the 'first' variable and
// the 'value' is stored in the second.
const auto& mkey{ it->first };
const auto& mvalue{ it->second };
// That or just grab the entire pair pointed
// to by the iterator.
const auto& pair{ *it };
}
else
{
// Key was not found..
}
m.find == m.end() // not found
If you want to use other API, then find go for m.count(c)>0
if (m.count("f")>0)
cout << " is an element of m.\n";
else
cout << " is not an element of m.\n";
I think you want map::find. If m.find("f") is equal to m.end(), then the key was not found. Otherwise, find returns an iterator pointing at the element found.
The error is because p.first is an iterator, which doesn't work for stream insertion. Change your last line to cout << (p.first)->first;. p is a pair of iterators, p.first is an iterator, p.first->first is the key string.
A map can only ever have one element for a given key, so equal_range isn't very useful. It's defined for map, because it's defined for all associative containers, but it's a lot more interesting for multimap.
template <typename T, typename Key>
bool key_exists(const T& container, const Key& key)
{
return (container.find(key) != std::end(container));
}
Of course if you wanted to get fancier you could always template out a function that also took a found function and a not found function, something like this:
template <typename T, typename Key, typename FoundFunction, typename NotFoundFunction>
void find_and_execute(const T& container, const Key& key, FoundFunction found_function, NotFoundFunction not_found_function)
{
auto& it = container.find(key);
if (it != std::end(container))
{
found_function(key, it->second);
}
else
{
not_found_function(key);
}
}
And use it like this:
std::map<int, int> some_map;
find_and_execute(some_map, 1,
[](int key, int value){ std::cout << "key " << key << " found, value: " << value << std::endl; },
[](int key){ std::cout << "key " << key << " not found" << std::endl; });
The downside to this is coming up with a good name, "find_and_execute" is awkward and I can't come up with anything better off the top of my head...
map<string, string> m;
check key exist or not, and return number of occurs(0/1 in map):
int num = m.count("f");
if (num>0) {
//found
} else {
// not found
}
check key exist or not, and return iterator:
map<string,string>::iterator mi = m.find("f");
if(mi != m.end()) {
//found
//do something to mi.
} else {
// not found
}
in your question, the error caused by bad operator<< overload, because p.first is map<string, string>, you can not print it out. try this:
if(p.first != p.second) {
cout << p.first->first << " " << p.first->second << endl;
}
Be careful in comparing the find result with the the end like for map 'm' as all answer have
done above
map::iterator i = m.find("f");
if (i == m.end())
{
}
else
{
}
you should not try and perform any operation such as printing the key or value with iterator i if its equal to m.end() else it will lead to segmentation fault.
Comparing the code of std::map::find and std::map::count, I'd say the first may yield some performance advantage:
const_iterator find(const key_type& _Keyval) const
{ // find an element in nonmutable sequence that matches _Keyval
const_iterator _Where = lower_bound(_Keyval); // Here one looks only for lower bound
return (_Where == end()
|| _DEBUG_LT_PRED(this->_Getcomp(),
_Keyval, this->_Key(_Where._Mynode()))
? end() : _Where);
}
size_type count(const key_type& _Keyval) const
{ // count all elements that match _Keyval
_Paircc _Ans = equal_range(_Keyval); // Here both lower and upper bounds are to be found, which is presumably slower.
size_type _Num = 0;
_Distance(_Ans.first, _Ans.second, _Num);
return (_Num);
}
I know this question already has some good answers but I think my solution is worth of sharing.
It works for both std::map and std::vector<std::pair<T, U>> and is available from C++11.
template <typename ForwardIterator, typename Key>
bool contains_key(ForwardIterator first, ForwardIterator last, Key const key) {
using ValueType = typename std::iterator_traits<ForwardIterator>::value_type;
auto search_result = std::find_if(
first, last,
[&key](ValueType const& item) {
return item.first == key;
}
);
if (search_result == last) {
return false;
} else {
return true;
}
}
map <int , char>::iterator itr;
for(itr = MyMap.begin() ; itr!= MyMap.end() ; itr++)
{
if (itr->second == 'c')
{
cout<<itr->first<<endl;
}
}
If you want to compare pair of map you can use this method:
typedef map<double, double> TestMap;
TestMap testMap;
pair<map<double,double>::iterator,bool> controlMapValues;
controlMapValues= testMap.insert(std::pair<double,double>(x,y));
if (controlMapValues.second == false )
{
TestMap::iterator it;
it = testMap.find(x);
if (it->second == y)
{
cout<<"Given value is already exist in Map"<<endl;
}
}
This is a useful technique.