I understand that std::map is (Key, Value) pairs.
I want to search through the values of a map. Let us say that I want to find the highest value among the values in the std::map. How can I do that ?
For example let me consider a map like this:
John -> 100
Jeffrey -> 200
Krishna -> 147
I think it will be similar to this , but I am not sure.
for (auto it=m.begin(); it!=m.end(); it++)
{
if (it->second == 500)
{
cout << "Found";
}
else {
continue;}
}
Instead of iterating through std::map, is there any other inbuilt method using which I can check if a value exists in a std::map with O(1) time complexity ?
Q1: How to check if a value exists in hashmap?
You need to iterate through and check if such item exists. You can use `std::find_if() with a lambda or do that through a loop. If you do that quite often you may want to index values as well (see below)
Q2: How to iterate through all the values in a map and find the largest value or the smallest value ?
Again you iterate through container and find it or you can use std::max_element() or std::min_element() with a lambda as well. Though if you need to access values in sorted order you may consider to use boost::multimap which will allow to access data using hashed index by name and provide sorted or hashed index(es) for values, though you should be aware of a price of every index added.
For the second question, use:
std::map<std::string, int> foo = {{"John",100},{"Jeffrey",200},{"Krishna",147}};
std::cout << std::max_element(foo.begin(), foo.end(), [](const auto& p1, const auto& p2){return p1.second < p2.second;})->first;
std::cout << std::min_element(foo.begin(), foo.end(), [](const auto& p1, const auto& p2){return p1.second < p2.second;})->first;
Use an adapted lambda with std::find_if, you should be able to find also if a value exists in a map (or hash table).
Related
I have vector of some data type (Let's say-int) and I need to push back only unique values from the file? I am new to use STL. So i don't know how can i do it using map as i read that map only takes unique values. If I simply push back, then it will take all the values irrespective of its uniqueness.
The correct container to use for unique values is either std::set or std::unordered_set:
std::set<int> s;
s.insert(4); // s has size 1
s.insert(5); // s has size 2
s.insert(4); // s still has size 2
If you want to use vector, you'd have to maintain it sorted, which is a lot more code and work, and doesn't have the nice characteristic of set that everybody knows the contents are unique:
void add_value(std::vector<int>& v, int value) {
// do a binary search to find value
std::vector<int>::iterator it = std::lower_bound(v.begin(), v.end(), value);
if (it != v.end() && *it == value) {
// duplicate - do nothing
}
else {
// insert our value here
v.insert(it, value);
}
}
... or I guess you could delete the duplicates at the end using a rarely-used algorithm (std::unique) that will probably raise some eyebrows:
void uniqify(std::vector<int>& v) {
std::sort(v.begin(), v.end());
v.erase(std::unique(v.begin(), v.end()), v.end());
}
[UPDATE] It has been pointed out to me that I completely misunderstood your question - and that you may have been looking for just which values occur exactly once - not a list of which values occur without duplicate. For that, the correct container to use is either a std::map or std::unordered_map - so you can associate a count with a particular key:
std::map<int, int> keyCounts;
int value;
while (fileStream >> value) { // or whatever
++keyCounts[value]; // operator[] gives us a reference to the value
// if it wasn't present before, it'll insert a default
// one - which for int is zero - so this handles
// both cases correctly
}
// Now, any key with value 1 is a unique key
// what you want to do with them is up to you
// e.g., let's put it in a vector
std::vector<int> uniq;
uniq.reserve(keyCounts.size());
for (std::map<int, int>::iterator it = keyCounts.begin(); it != keyCounts.end(); ++it)
{
if (it->second == 1) {
uniq.push_back(it->first);
}
}
A std::map will let you handle a mapping of unique keys to some values (which may or may not be unique). Math-wise, You may see it as a surjective function from the set of keys to the set of values of your dataset.
If your goal is to keep unique indices (or keys), then std::map is what you need. Otherwise, use std::set to store unique values.
Now, to keep only unique values from your dataset, you basically want to remove values which appear more than once. The simplest algorithm is to add values from the file as keys in a map, with its corresponding value being a counter for the number of occurrences of that entry in the file. Initialize a counter to 1 the first time the value is met in the file, and increment it each time it is met again. After having parsed the whole file, simply keep the keys whose values are exactly 1.
Counting the values:
template <typename key>
void count(std::istream &is, std::map<key,int> &map){
while (!is.eof() && is.good()){
key << is;
auto it = map.find(key);
if (it == map.end())
map[key] = 1;
else (*it)++;
}
}
The above assumes that the << has been overloaded to extract values from the stream sequentially. You will have to adapt the algorithm to fit your own way of parsing the data.
Filtering the resulting map to keep unique values can be achieved with std::remove_if and a function returning true when the counter is above 1:
The function:
bool duplicate (std::const_iterator<int> &it){ return *it > 1;}
The map filtering:
std::remove_if (map.begin(), map.end(), duplicate);
I need to sort a std::map by value, then by key. The map contains data like the following:
1 realistically
8 really
4 reason
3 reasonable
1 reasonably
1 reassemble
1 reassembled
2 recognize
92 record
48 records
7 recs
I need to get the values in order, but the kicker is that the keys need to be in alphabetical order after the values are in order. How can I do this?
std::map will sort its elements by keys. It doesn't care about the values when sorting.
You can use std::vector<std::pair<K,V>> then sort it using std::sort followed by std::stable_sort:
std::vector<std::pair<K,V>> items;
//fill items
//sort by value using std::sort
std::sort(items.begin(), items.end(), value_comparer);
//sort by key using std::stable_sort
std::stable_sort(items.begin(), items.end(), key_comparer);
The first sort should use std::sort since it is nlog(n), and then use std::stable_sort which is n(log(n))^2 in the worst case.
Note that while std::sort is chosen for performance reason, std::stable_sort is needed for correct ordering, as you want the order-by-value to be preserved.
#gsf noted in the comment, you could use only std::sort if you choose a comparer which compares values first, and IF they're equal, sort the keys.
auto cmp = [](std::pair<K,V> const & a, std::pair<K,V> const & b)
{
return a.second != b.second? a.second < b.second : a.first < b.first;
};
std::sort(items.begin(), items.end(), cmp);
That should be efficient.
But wait, there is a better approach: store std::pair<V,K> instead of std::pair<K,V> and then you don't need any comparer at all — the standard comparer for std::pair would be enough, as it compares first (which is V) first then second which is K:
std::vector<std::pair<V,K>> items;
//...
std::sort(items.begin(), items.end());
That should work great.
You can use std::set instead of std::map.
You can store both key and value in std::pair and the type of container will look like this:
std::set< std::pair<int, std::string> > items;
std::set will sort it's values both by original keys and values that were stored in std::map.
As explained in Nawaz's answer, you cannot sort your map by itself as you need it, because std::map sorts its elements based on the keys only. So, you need a different container, but if you have to stick to your map, then you can still copy its content (temporarily) into another data structure.
I think, the best solution is to use a std::set storing flipped key-value pairs as presented in ks1322's answer.
The std::set is sorted by default and the order of the pairs is exactly as you need it:
3) If lhs.first<rhs.first, returns true. Otherwise, if rhs.first<lhs.first, returns false. Otherwise, if lhs.second<rhs.second, returns true. Otherwise, returns false.
This way you don't need an additional sorting step and the resulting code is quite short:
std::map<std::string, int> m; // Your original map.
m["realistically"] = 1;
m["really"] = 8;
m["reason"] = 4;
m["reasonable"] = 3;
m["reasonably"] = 1;
m["reassemble"] = 1;
m["reassembled"] = 1;
m["recognize"] = 2;
m["record"] = 92;
m["records"] = 48;
m["recs"] = 7;
std::set<std::pair<int, std::string>> s; // The new (temporary) container.
for (auto const &kv : m)
s.emplace(kv.second, kv.first); // Flip the pairs.
for (auto const &vk : s)
std::cout << std::setw(3) << vk.first << std::setw(15) << vk.second << std::endl;
Output:
1 realistically
1 reasonably
1 reassemble
1 reassembled
2 recognize
3 reasonable
4 reason
7 recs
8 really
48 records
92 record
Code on Ideone
Note: Since C++17 you can use range-based for loops together with structured bindings for iterating over a map.
As a result, the code for copying your map becomes even shorter and more readable:
for (auto const &[k, v] : m)
s.emplace(v, k); // Flip the pairs.
std::map already sorts the values using a predicate you define or std::less if you don't provide one. std::set will also store items in order of the of a define comparator. However neither set nor map allow you to have multiple keys. I would suggest defining a std::map<int,std::set<string> if you want to accomplish this using your data structure alone. You should also realize that std::less for string will sort lexicographically not alphabetically.
EDIT: The other two answers make a good point. I'm assuming that you want to order them into some other structure, or in order to print them out.
"Best" can mean a number of different things. Do you mean "easiest," "fastest," "most efficient," "least code," "most readable?"
The most obvious approach is to loop through twice. On the first pass, order the values:
if(current_value > examined_value)
{
current_value = examined_value
(and then swap them, however you like)
}
Then on the second pass, alphabetize the words, but only if their values match.
if(current_value == examined_value)
{
(alphabetize the two)
}
Strictly speaking, this is a "bubble sort" which is slow because every time you make a swap, you have to start over. One "pass" is finished when you get through the whole list without making any swaps.
There are other sorting algorithms, but the principle would be the same: order by value, then alphabetize.
assumend I have a (filled) list
std::list<std::pair<int,otherobject>> myList;
and want to find() the first element within this list, where int has a specific value - how can I do that?
To explain it a bit further:
I want to append these pairs to the list with an int that identifies otherobject but is not unique. The order where these int/otherobject pairs arrive has to be kept.
When an int is found during access to elements of this list the first occurence of that int has to be given back (and removed).
Thanks!
I think I'd use the standard find_if algorithm:
auto pos = std::find_if(myList.begin(), myList.end(),
[value](std::pair<int, otherobject> const &b) {
return b.first == value;
});
That gives an iterator to the element with the required value -- from there, you can copy the value, delete the value, etc., just like with any other iterator.
According to your need the better option would be to use a multimap.
In you case it would give :
std::multimap<int, otherobject> myMultiMap;
Then when looking for otherobjects linked to a int ( myInt) you'll do :
std::pair<std::multimap<int, otherobject>::iterator, std::multimap<int, otherobject>::iterator> result = myMultiMap.equal_range(myInt);
for (std::multimap<int,otherobject>::iterator iter=result.first; iter!=result.second; ++iter)
{
std::cout << it->second;
}
This is a STL container so you'll easilly find online documentation.
Let's assume I have a vector<node> containing 10000 objects:
vect[0] to vect[9999]
struct node
{
int data;
};
And let's say I want to find the vector id that contain this data ("444"), which happens to be in node 99.
Do I really have to do a for-loop to loop through all the elements then use
if (data == c[i].data)
Or is there a quicker way? Consider that my data is distinct and won't repeat in other nodes.
For this answer I am assuming that you've made an informed decision to use a std::vector over the other containers available.
Do I really have to do a for-loop to loop through all the elements?
No, you do not have to roll a for-loop to find an element. The idiomatic way of finding an element in a container is to use an algorithm from the standard library. Whether you should roll your own really depends on the situation.
To help you decide...
Alternative 1:
std::find() requires a that there is a suitable equality comparator for your node data type, which may be as simple as this:
bool operator ==(node const& l, node const& r)
{
return l.data == r.data;
}
Then, given a required node, you can search for the element. This returns an iterator (or a pointer if you're using a plain old array). If you need the index, this requires a little calculation:
auto i = std::find(v.begin(), v.end(), required);
if (i != v.end())
{
std::cout << i->data << " found at index " << i - v.begin() << std::endl;
}
else
{
std::cout << "Item not found" << std::endl;
}
Alternative 2:
If creating a node is too expensive or you don't have an equality operator, a better approach would be to use std::find_if(), which takes a predicate (here I use a lambda because it's succinct, but you could use a functor like in this answer):
// Alternative linear search, using a predicate...
auto i = std::find_if(v.begin(), v.end(), [](node const& n){return n.data == 444;});
if (i != v.end())
{
std::cout << i->data << " found at index " << i - v.begin() << std::endl;
}
else
{
std::cout << "Item not found" << std::endl;
}
Or is there a quicker way?
Again, it depends. std::find() and std::find_if() run in linear time (O(n)), the same as your for-loop.
That said, using std::find() or std::find_if() won't involve random access or indexing into the container (they use iterators) but they may require a little bit of extra code compared with your for-loop.
Alternative 3:
If running time is critical and your array is sorted (say with std::sort()), you could perform a binary-search, which runs in logarithmic time (O(log n)). std::lower_bound() implements a binary search for the first element that is not less than the given value. It does not take a predicate unfortunately but requires a suitable less-than comparator for your node data type, such as:
bool operator <(node const& l, node const& r)
{
return l.data < r.data;
}
The invocation is similar to std::find() and returns an iterator, but requires an extra check:
auto i = std::lower_bound(v.begin(), v.end(), required);
if (i != v.end() && i->data == required.data)
{
std::cout << i->data << " found at index " << i - v.begin() << std::endl;
}
else
{
std::cout << "Item not found" << std::endl;
}
These functions from the Algorithms Library work with any container supplying an iterator, so switching to another container from std::vector would be quick and easy to test and to maintain.
The decision is yours!
[See a demonstration here.]
You should use std::find. You can't get faster than linear complexity (O(n)) if you know nothing about the vector beforehand (like it being sorted).
If you want to find elements in the container then vector is not the right data-structure. You should use an ordered container such as std::set or std::map. Since elements in these containers are kept ordered (sorted), we can find elements in O(log (n)) time as opposed to linear time for unordered containers.
Use std::find :
vector<int>::Iterator it = find (vect.begin(), vect.end(), 444);
Note that If you have sorted vector, you can make it faster.
A neat solution would be to add an extra int index member to the node struct to provide data-to-index mapping when you have an instance of the struct. In such a case, you should probably wrap std::vector in a NodeVector class which will handle the updating of indices when, say, you remove an item (it's enough to subtract 1 from elements' indices which preceed the element being removed in such a case) etc. If the vector doesn't change the number of elements, that's not even an issue. Other than that, if you can't have an instance of the struct grow in size, use std::map. Iterating over the containter to find one item is not very smart, unless you need to do it very rarely and making anything complicated isn't worth the trouble.
Say I have more than one key with the same value in a map. Then in that case how do I retrieve all keys that matches a query.
Or, Is there any possibility to tell find operation to search after a specific value.
I am using an std::map, C++.
Would something like this work for you:
void FindKeysWithValue(Value aValue, list<Key>& aList)
{
aList.clear();
for_each(iMap.begin(), iMap.end(), [&] (const pair<Key, Value>& aPair)
{
if (aPair.second == aValue)
{
aList.push_back(aPair.first);
}
});
}
The associative containers probably won't help you too much because for std::map<K, V> the key happens to be unique and chances that your chosen query matches the ordering relation you used may not be too high. If the order matches, you can use the std::map<K, V> members lower_bound() and upper_bound(). For std::multimap<K, V> you can also use equal_range().
In general, i.e., if you query isn't really related to the order, you can use std::copy_if() to get a sequence of objects matching a predicate:
Other other;
// ...
std::vector<Other::value_type> matches;
std::copy_if(other.begin(), other.end(),
std::back_inserter(matches), predicate);
When copying the elements is too expensive, you should probably consider using std:find_if() instead:
for (auto it(other.begin());
other.end() != (it = std::find_if(it, other.end(), predicate));
++it) {
// do something with it
}
The only way is to iterate over map.
this link may be useful: Reverse map lookup
Provided you want quick access and you don't mind using some more space, then you maintain another map that gets stored as value, key. In your case, you would need to handle the duplicate values (that you will be storing as keys).
Not a great idea but definitely an option.
A map is meant for efficient lookup of keys. Lookup based on values is not efficient, and you basically have to iterate through the map, extracting matches yourself:
for(map<A,B>::iterator i = m.begin(); i != m.end(); i++)
if(i->second == foo)
you_found_a_match();
If you intend to do this often, you can build up a multimap mapping the other way, so you can efficiently perform a value-based lookup:
multimap<B,A> reverse;
for(map<A,B>::iterator i = m.begin(); i != m.end(); i++)
reverse.insert(pair<B,A>(i->second,i->first));
You can now easily find the keys with a given value value:
matches = reverse.equal_range(value);
for(multimap<B,A>::iterator i = matches.first; i != matches.second; i++)
A & key = i->second;
If these maps aren't going to grow continuously, it may be more efficient to simply maintain a vector > instead, define a comparator for it based on the value, and use equal_range on that instead.