Find unique values in map<string, vector<string>>? - c++

I'm trying to create a basic search function. Right now, I have the function to set up to accept three arguments: map<string, vector<string>>, const string& key, and bool unique.
My goal is to have query check if key is in the map, and if it is with bool unique = false just output all the values associated with the key, duplicates and all.
However, I need to also have the ability to output just the unique values associated with the key if bool unique = true
I have the latter part working exactly as a want it, but I'm struggling with outputting the unique values. How do I go about this? Is it possible to do so without creating a new vector or altering the map?
Apologies if anything is unclear, I'm happy to clarify if needed. Thank you.
void query(const map<string, vector<string>>& map, const string& key, bool unique) {
const auto pair = map.equal_range(key);
if(map.count(key)){
for( auto iter = pair.first ; iter != pair.second ; ++iter ){
for( const auto& str : iter->second ) {
if (!unique){
cout << " " << key << " ";
cout << str << "\n";
} else if (unique) {
// output key + unique values
}
}
}
} else {
cout << "<Not found>\n";
}

Related

Checking for duplicate values associated with key in map?

I'm struggling with outputting unique values of a map<string, vector<string>> I have. Right now, I have a map, and iterate through it, with the goal of outputting only unique values associated with the specified key.
I do need to keep the duplicate values, or else I'd just remove the dups :)
After looking at this post, my set up is like the following:
for( const auto& pair : myMap ){
for( std::size_t i = 0; i < pair.second.size(); ++i ) {
bool notMatch = (pair.second[i] != pair.second[i+1]){
if (pair.first == key && notMatch){
cout << key << " : ";
cout << pair.second[i] << " - at index - " << i << "\n";
}
}
}
I then get an output along the lines of :
"key : value - at index - 6"
"key : value - at index - 10"
My initial thought was that one of the elements might have some extra characters or something, which would make sense as to why the duplicate elements are not being seen as equal.
But when doing a simple check of -
if (pair.second[6] == pair.second[10]){
cout << "They are equal";
} else {
cout << "They are NOT equal";
}
It confirms and returns that the two elements are in fact equal. Since the elements are equal, I'm struggling to understand why bool notMatch = (pair.second[i] != pair.second[i+1]) does not consider them to be equal.
Apologies if this was posted incorrectly, I'll edit if necessary.
Thanks for your help
Building on #Tzalumen's comment, you could insert the values in a set or unordered set and compare the size to the original vector:
for(const auto& pair : myMap){
unordered_set<string> s(pair.second.begin(), pair.second.end());
if (s.size() == pair.second.size()) {
cout << "value has unique elements" << endl;
} else {
cout << "value has duplicate elements" << endl;
}
}
If the set's size is smaller than the vector's size, you know the vector has duplicates.
If you don't want duplicates anywhere, why not have a std::map<std::string, std::set<std::string>> in the first place?

Using lower_bound on nested map

I have a map that looks like
map<string , map<int,int>>
the string contains name of a student, the nested map contains ID as key and age as value. When I print the map, it al prints values as it should.
However, I want to find a students with a certain ID and lower. I tried using lower_bound using:
for( auto &x : class ){
auto it = x.second.upper_bound(some_number);
for( ; it != x .second.begin() ; --it){
cout << x.first << " = " << << it -> first << " " <<it -> second << endl;
}
}
This indeed prints right names of students, but their IDs and ages are just zeros or random numbers, what is causing this behavior? It works when I just print it.
I tried to found out about this on cpp map reference but found nothing.
Following code solves your problem:
for( auto &x : Class ){
auto it = x.second.upper_bound(some_number);
while(it!=x.second.begin()){
it=prev(it);
cout<< x.first<< " = "<< it->first<< " "<< it->second<< endl;
}
}
Refer std::map::upper_bound
What above code does is, first it finds the iterator with id strictly greater than some_number. Now because we want to print "students with a certain ID and lower", we print all the id's lower than the return value of upper_bound.
The stopping condition is that if iterator is itself x.second.begin(), that means now we don't have any id's smaller than it.
Plus your data structure is strange, you should have student ID as your primary index.
map<int, pair<string,int> > would be more appropriate data structure. (Assuming unique id's which is mostly the case).
Although, you could do lot better using OOP concepts.
What you see is probably undefined behaviour, std::map::upper_bound returns also end iterator under some conditions and from your code it does not look like you check for this condition. Also you should not use class keyword as variable name for your map, I am preety sure it does not compile. Below is a sample code that should work with no UB and print all IDs less than some number including this ID:
http://coliru.stacked-crooked.com/a/efae1ae4faa3e656
map< string , map<int,int>> classes ={
{ "k1", {{1,1},{2,2},{3,3}} }
};
//int class;
int some_number = 4;
for( auto &x : classes ){
auto it_num_end = x.second.upper_bound(some_number); // some numberis just variable that contains number
for( auto it = x.second.begin(); it != it_num_end ; ++it){
cout << x.first << " = " << it -> first << " " <<it -> second << endl;
}
}

How to insert key and value to map that is inside another map in cpp?

I have this map :
std::map<std::string, std::map<std::string, std::string> > objects;
As far as I know, when I write objects[key] it will replace the value of the key already exists, but here I want it to add to the second map if the pair doesn't exist.
Example:
Current MAP :("key1",map(<"key_a","value_a">,<"key_c","value_d">))
input:
"key1", "key_e","value_f"
relsult:
("key1",map(<"key_a","value_a">,<"key_c","value_d">,<"key_e","value_f">))
input :
"key1", "key_e","value_g"
(replace the value from "value_f" to "value_g")
result:
("key1",map(<"key_a","value_a">,<"key_c","value_d">,<"key_e","value_g">))
and if I get "key2" -> it will insert new key with empty map
can I do this :
objects[key].insert(std::make_pair(key2,value2))
it didn't work for some reason
can you please advise?
As far as I know, when I write objects[key] it will replace the value of the key already exists, but here I want it to add to the second map if the pair doesn't exist.
That's wrong: if you write objects[key] is doesn't replace anything, ever; rather:
If objects doesn't already contain key, it inserts a new pair in the map with that key and for the value it uses a default constructed (in this case empty) std::map<std::string, std::string>. It then returns a reference to the value.
If objects does contain key, it returns a reference to the existing value (the "inner" map) without changing or replacing anything.
can I do this: objects[key].insert(std::make_pair(key2,value2))
You could, but if [key][key2] already existing that wouldn't update the value to be value2, which I assume is what you want and why you say "it didn't work".
All you have to do is:
objects[key][key2] = value2;
Because objects[key] never replaces an existing map, but default-constructs one if key's missing, then key2 either finds the existing entry in the "inner" map that needs updating or creates it, this all hangs together (if I've understood your requirement correctly).
maybe you mean something like this?
http://www.cplusplus.com/reference/map/map/operator%5B%5D/
http://www.cplusplus.com/reference/map/map/count/
std::map<std::string, std::map<std::string, std::string> > objects;
if (objects.count("your_key") > 0)
{ // insert a key ("another_key") into map
objects["your_key"]["another_key"] = "some value";
} else
{ // create the new map for the key
objects["your_key"] = std::map<std::string, std::string>();
}
You can use method insert. Here is a demonstrative program
#include <iostream>
#include <string>
#include <map>
int main()
{
std::map<std::string, std::map<std::string, std::string>> objects;
objects["key1"] = { { "key_a", "value_a" }, { "key_c", "value_d" } };
objects["key1"].insert( { "key_e", "value_f" } );
for ( const auto &p1 : objects )
{
std::cout << "\"" << p1.first << "\": ";
for ( const auto &p2 : p1.second )
{
std::cout << "{ \"" << p2.first
<< "\", \"" << p2.second
<< "\" }" << ' ';
}
std::cout << std::endl;
}
objects["key1"]["key_e"] = "value_g";
for ( const auto &p1 : objects )
{
std::cout << "\"" << p1.first << "\": ";
for ( const auto &p2 : p1.second )
{
std::cout << "{ \"" << p2.first
<< "\", \"" << p2.second
<< "\" }" << ' ';
}
std::cout << std::endl;
}
return 0;
}
The program output is
"key1": { "key_a", "value_a" } { "key_c", "value_d" } { "key_e", "value_f" }
"key1": { "key_a", "value_a" } { "key_c", "value_d" } { "key_e", "value_g" }

Iterating in to the set of strings which are the values in map

#include <iostream>
using namespace std;
void insertValue(map<string, set<string> >& myMap,
string const& key,
string const& value)
{
// Check whether there is already a set given the key.
// If so, insert to the existing set.
// Otherwise, create a set and add it to the map.
map<string, set<string> >::iterator found = myMap.find(key);
if (found != myMap.end())
{
cout << "Adding '" << value << "' to an existing set of " << key << "s.\n";
found->second.insert(value);
}
else
{
cout << "Adding '" << value << "' to a new set of " << key << "s.\n";
set<string> temp;
temp.insert(value);
myMap.insert(make_pair(key, temp));
}
}
int main()
{
map<string, set<string> > filemap;
insertValue(mymap, "file1", "path1");
insertValue(mymap, "file1", "path2");
insertValue(mymap, "file1", "path3");
insertValue(mymap, "file2", "path1");
insertValue(mymap, "file3", "path2");
return 0;
}
Can anyone tell me how I can iterate through the set of strings, given a key in the above map???? or do I have to place an iterator in the value ....I can't understand how can I go this this further
The easiest way to iterate over the map is by using a range-based for instead of using iterators
for(auto const& kv : mymap) {
for(auto const& v : kv.second) {
std::cout << kv.first << ": " << v << '\n';
}
}
kv is a const& to your map's value_type, which is std::pair<const std::string, std::set<std::string>>. The nested for statement is then iterating over the second element in the pair.
And if you really want to use iterators, then use this
for(auto miter = mymap.cbegin(); miter != mymap.cend(); ++miter) {
for(auto siter = miter->second.cbegin(); siter != miter->second.cend(); ++siter) {
std::cout << miter->first << ": " << *siter << '\n';
}
}
That aside, your function for inserting values can be simplified quite a bit. There's no need to check whether an element already exists in a map before inserting a value because map::operator[] will construct the key you pass to it if one doesn't already exist, and the corresponding value type will be value initialized. So your insertValue function becomes a one-liner.
void insertValue(map<string, set<string> >& myMap,
string const& key,
string const& value)
{
myMap[key].insert(value); // default construct a set for a new key
}
Finally, unless you need the values corresponding to a key be ordered, you can use a multimap instead. This container is just like a map, but you can have several values corresponding to a single key value. However, unlike your solution, the order of the values which have identical keys is the order of their insertion.
Live demo

c++ map finding value and associated key

I develop one program in c++ in which i have to find key in stl map by using values.
But values assigned to key is the 5 tuples (srcip,port,destip,port,srcno)
Now i want to check in map whether there is key assosiated with values.
I am trying something like this.
But its showing error like
wrong number of template argument.
Note(In my program in pair key->Value) value consist of tuple of 5 variable.
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type,typename T::mapped_type,bool>
{
public:
bool operator() (typename T::value_type &pair,typename T::mapped_type i)
{
return pair.second == i;
}
}
class Values
{
private:
std::string C_addr;
int C_port;
std::string S_addr;
int S_port;
int C_ID;
public:
Values(std::string,int,std::string,int,int);
void printValues();
};
Values :: Values(std::string Caddr,int Cport,std::string Saddr,int Sport,int Cid)
{
C_addr=Caddr;
C_port=Cport;
S_addr=Saddr;
S_port=Sport;
C_ID=Cid;
}
void Values::printValues()
{
cout << C_addr<<":" <<C_port<<":" << S_addr <<":" <<S_port << ":"<<C_ID <<endl;
}
//In main
{
typedef std::map<int, Values> itemsType;
itemsType items;
Values connection (inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port),clientID);
std::map<std::int,Values>::iterator it = std::find_if( items.begin(), items.end(), std::bind2nd(map_data_compare<itemsType>(),connection));
if ( it != items.end() )
{
assert( connection == it->second);
std::cout << "Found index:" << it->first << " for values:" << it->second << std::endl;
}
else
{
std::cout << "Did not find index for values:" << connection <<endl;
}
I develop one program in c++ in which i have to find key in stl map by using values.
That's not what maps are meant for. If you need that kind of access, I recommend Boost.Bimap
If the 'key' must be unique, maybe you can try combine the key and value into a std::pair and push them into std::set.
Otherwise you should set your key as value and value as key since you seems mainly use your original value as what we treat to a "key". Then you could use the built-in map::find() function