how to get matching key using the value in a map C++ - c++

I have a map with a struct as a value type
map<int id, struct_t*> table
struct_t
{
int prev;
int wt;
string name;
}
Using only prev, I need to find the corresponding id. Thanks so much in advance!
EDIT:
int key=0;
for(auto it = table.begin(); it != table.end(); ++it)
{
if(table[(*it).first].prev == ?)
}
This is how my map data looks like:
id prev abundance thing
1573 -1 0 book
1864 1573 39 beds
2075 1864 41 tray
1760 2075 46 cups
For each id, I need to find the NEXT matching id. So, for 1573 from the prev column I need to find a matching 'id' which is 1864. Also, std::next doesn't work because the data set can have the matching ids not necessarily in the next element.Hope this helps!
PLEASE PLEASE help me!!! MY boss is already disappointed that I'm taking so much time to learn C++ (its been 3 weeks already!)

If you've got a modern compiler (supports lambdas), you can do the following:
const int prevToFind = 10;
auto findResult = std::find_if(std::begin(table), std::end(table), [&](const std::pair<int, struct_t*> &pair)
{
return pair.second->prev == prevToFind;
});
int foundKey = 0; // You might want to initialise this to a value you know is invalid in your map
struct_t *foundValue = nullptr
if (findResult != std::end(table))
{
foundKey = findResult->first;
foundValue = findResult->second;
// Now do something with the key or value!
}
Let me know if you have an older compiler, and I can update the example to use a predicate class instead.

Simple loop can do it:
#include <map>
#include <string>
#include <iostream>
int main()
{
std::map<int, std::string> m = {
std::make_pair(0, "zero"), std::make_pair(1, "one"), std::make_pair(2, "two")
};
int key = 0;
for (auto &i : m) {
if (i.second == "two") {
key = i.first;
break; // to stop searching
}
}
std::cout << key << std::endl;
}
Of course you need to set up your own if-statement for searching.
Please note, boost bidirectional map could be a solution (boost::bimap)

Looping over the map of course does the trick, but you may want to consider using a second map as an index:
map<int,int> table_idx;
Whenever you add new entries to table you will need to update table_idx as well, storing the id that corresponds to every prev. table_idx will then allow you to reverse-lookup the id in log(N) time:
int prev_for_id = table_idx[id];

Im getting a feeling that you are a beginner so it would be nice if you would tell us what are you trying to do because maybe you are trying to solve a wrong problem.
Like noted maps are designed to be searched by the key, not value.
That being said if you insist on searching the map this way you will problably wanna check out Boost Bimap.

Is it not possible to generate a reverse map with something like that:
typedef std::map<int, struct_t*> map_t;
typedef std::map<struct_t*, int> reverse_map_t;
reverse_map_t get_reverse( map_t m )
{
reverse_map_t r;
for( const auto& p: m )
{
r[p.second] = p.first;
}
return r;
}

Related

Optimized way to partition a class of objects based on an attribute in C++

I have a class that has million of items and each item has a label of type int. I need to partition items based on their similar labels, so at the end I return a vector<MyClass>. First, I sort all items based on their label. Then, in a for loop I compare each label value with previous one and if its the same I store it in a myclass_temp until label != previous_label. If label != previous_label I add this myclass_temp to the vector<MyClass>, and I erase myclass_temp. I think the code is self-explained.
The program works fine, but it is slow, is there a better way to speed it up? I believe because I sort the items in the beginning, there should be a faster way to simply partition items with similar labels.
Second question is how to calculate O score for this algorithm and any suggested faster solution?
please feel free to correct my code.
vector <MyClass> PartitionByLabels(MyClass &myclass){
/// sort MyClass items based on label number
printf ("Sorting items by label number... \n");
std::sort(myclass.begin(), myclass.end(), compare_labels);
vector <MyClass> myClasses_vec;
MyClass myclass_temp;
int previous_label=0, label=0;
int total_items;
/// partition myclass items based on similar labels
for (int i=0; i < myclass.size(); i++){
label = myclass[i].label;
if (label == previous_label){
myclass_temp.push_back(myclass[i]);
previous_label = label;
/// add the last similar items
if (i == myclass.size()-1){
myClasses_vec.push_back(myclass_temp);
total_items +=myclass_temp.size();
}
} else{
myClasses_vec.push_back(myclass_temp);
total_items +=myclass_temp.size();
myclass_temp.EraseItems();
myclass_temp.push_back(myclass[i]);
previous_label = label;
}
}
printf("Total number of items: %d \n", total_items);
return myClasses_vec;
}
This algorithm should do it. I removed the templates to make it easier to check on godbolt.
Should be easy enough to put back in.
The O score for this method is that of std::sort - O(N.log(N))
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
struct thing
{
std::string label;
std::string value;
};
using MyClass = std::vector<thing>;
using Partitions = std::vector<MyClass>;
auto compare_labels = [](thing const& l, thing const& r) {
return l.label < r.label;
};
// pass by value - we need a copy anyway and we might get copy elision
Partitions PartitionByLabels(MyClass myclass){
/// sort MyClass items based on label number
std::sort(myclass.begin(), myclass.end(), compare_labels);
Partitions result;
auto first = myclass.begin();
auto last = myclass.end();
// because the range is sorted, we can partition it in linear time.
// choosing the correct algorithm is always the best optimisation
while (first != last)
{
auto next = std::find_if(first, last, [&first](auto const& x) { return x.label != first->label; });
// let's move the items - that should speed things up a little
// this is safe because we took a copy
result.push_back(MyClass(std::make_move_iterator(first),
std::make_move_iterator(next)));
first = next;
}
return result;
}
We can of course do better with unordered maps, if:
the label is hashable and equality-comparable
we don't need to order the output (if we did, we'd use a multimap instead)
The O-score for this method is linear time O(N)
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
#include <unordered_map>
struct thing
{
std::string label;
std::string value;
};
using MyClass = std::vector<thing>;
using Partitions = std::vector<MyClass>;
// pass by value - we need a copy anyway and we might get copy elision
Partitions PartitionByLabels(MyClass const& myclass){
using object_type = MyClass::value_type;
using label_type = decltype(std::declval<object_type>().label);
using value_type = decltype(std::declval<object_type>().value);
std::unordered_multimap<label_type, value_type> inter;
for(auto&& x : myclass) {
inter.emplace(x.label, x.value);
}
Partitions result;
auto first = inter.begin();
auto last = inter.end();
while (first != last)
{
auto range = inter.equal_range(first->first);
MyClass tmp;
tmp.reserve(std::distance(range.first, range.second));
for (auto i = range.first ; i != range.second ; ++i) {
tmp.push_back(object_type{i->first, std::move(i->second)});
}
result.push_back(std::move(tmp));
first = range.second;
}
return result;
}
Why not create a map from ints to vectors, iterate through the original vector once, adding each MyClass object to TheMap[myclass[i].label]? It takes your average runtime from f(n + n*log(n)) to f(n).

Counting number of occurrences in a range within an unordered_map

I have my unordered_map set up as:
unordered_map<int, deque<my_struct>> table;
When I read values to my program, I usually do:
table[int].push_back(obj);
What I want to be able to do is if I'm given 2 integer variables, I want to be able to find the number of keys that occur between the two.
So if in my table I have code like
table[49].push_back(obj);
table[59].push_back(obj);
table[60].push_back(obj);
If I execute my search function(which I'm currently trying to write) to look between the key values of 45 and 65, I should have 3 results.
I'm not exactly sure how to go about it in an efficient manner. Any ideas would be helpful. Than you.
If you are using a std::unordered_map I don't think you have a choice but to loop over all integers 45 to 65 and use find to check if the key exists in the unordered_map:
using my_table = std::unordered_map<int, std::deque<my_struct>>;
int count(const my_table& table, int begin, int end) {
int sum = 0;
for (int i = begin; i != end; ++i) {
auto find_result = table.find(i);
if (find_result != table.end())
sum++;
}
return sum;
}
But this may not be very efficient. If you use a std::map instead the elements are ordered so this can be achieved more efficiently:
using my_table = std::map<int, std::deque<my_struct>>;
int count(const my_table& table, int begin, int end) {
auto begin_itr = table.lower_bound(begin);
if (begin_itr == table.end())
return 0;
auto end_itr = table.lower_bound(end);
return std::distance(begin_itr, end_itr);
}
I've used the std::map::lower_bound function.
Depending on how sparse your map is you might even consider using something like std::vector<std::deque<my_struct>> as a flat map.
Live demo.

Using iterators on maps

map<double, LatLon> closestPOI;
map<double, LatLon> ::iterator iterPOI = closestPOI.begin();
I made a tree that is keyed by distance between two points. I need to find the 3 points in this tree that are the smallest (3 smallest distances). I declared an iterator and initialized it to point at the root (I'm not sure if that was necessary but it didn't solve my problem). I tried using advance(iterPOI, 1) to increment the iterator but that didn't work either. How can I find these 3 points and access their values?
Note: Yes I know that the 3 nodes I want are the root and its kids (since they have the smallest distances)
Usually you use a for() loop to iterate the map:
for(map<double, LatLon> ::iterator iterPOI = closestPOI.begin();
iterPOI != closestPOI.end();
++iterPOI) {
// access the iterator's key: iterPOI->first ...
// access the iterator's value: iterPOI->second ...
}
To iterate over a map you can do something like this: (assuming you're using anything over gcc 4.8.2)
map<double, LatLon> closestPOI;
// if you don't have gcc 4.8.2 then do what you did with the iterator in your question...
for(auto i_poi = closestPOI.begin(); i_poi != closestPOI.end(); ++i)
{
// to get at the double you do: i_poi->first
// to get at the LatLon you do: i_poi->second
}
Hope that helps a bit
Here is an example of getting the first (i.e, smallest key) three elements of a map. I've aliased LatLong to int just as an example, so you can see it in action here:
#include <iostream>
#include <map>
#include <vector>
using LatLon = int;
int main()
{
std::map<double, LatLon> map { { 1.0d, 1 }, { 2.0d, 2 }, { 3.0d, 3 }, { 0.5d, 4 } };
// Get the three closest points and store them in a vector
std::vector<LatLon> closest;
for ( const auto& pair : map ) {
if ( closest.size() >= 3 )
break;
closest.push_back(pair.second);
}
// Do something with the three closest points
for ( auto latlon : closest )
std::cout << latlon << '\n';
return 0;
}
Note that if there are less than 3 points in your map to begin with, your closest vector will also have less than 3 elements.

To find duplicate entry in c++ using 2D Vector (std::vector)

I wrote a program to find duplicate entry in a table. I am a beginner in C++, hence I don't know how this program is working efficient. Is there any other idea to write this program? Here I have 3 tables (2D Vector), that they are 1)aRecord_arr 2)mainTable and 3)idxTable. idxtable is use to identify the keys to check duplicate entry. aRecord_arr table to be add in maintable. If it is already exist in maintable, it will show the error "Duplicate Entry". So Check this program, and give your suggestions.
typedef vector<string> rec_t;
typedef vector<rec_t> tab_t;
typedef vector<int> cn_t;
int main()
{
tab_t aRecord_arr= { {"a","apple","fruit"},
{"b","banana","fruit"} };
tab_t mainTable = { {"o","orange","fruit"},
{"p","pineapple","fruit"},
{"b","banana","fruit"},
{"m","melon","fruit"},
{"a","apple","fruit"},
{"g","guava","fruit"} };
tab_t idxTable = { {"code","k"},
{"name","k"},
{"category","n"}};
size_t Num_aRecords = aRecord_arr.size();
int idxSize = idxTable.size();
int mainSize = mainTable.size();
rec_t r1;
rec_t r2;
tab_t t1,t2;
cn_t idx;
for(int i=0;i<idxSize;i++)
{
if(idxTable[i][1]=="k")
{
idx.push_back(i);
}
}
for(size_t j=0;j<Num_aRecords;j++)
{
for(unsigned int id=0;id<idx.size();id++)
{
r1.push_back(aRecord_arr[j][idx[id]]);
}
t1.push_back(std::move(r1));
}
for(int j=0;j<mainSize;j++)
{
for(unsigned int id=0;id<idx.size();id++)
{
r2.push_back(mainTable[j][idx[id]]);
}
t2.push_back(std::move(r2));
}
for(size_t i=0;i<t1.size();i++)
{
for(size_t j=0;j<t2.size();j++)
{
if(t1[i]==t2[j])
{
cout<<"Duplicate Entry"<<endl;
exit(0);
}
}
}
}
If you want to avoid duplicate entries in an array, you should consider using a std::setinstead.
What you want is probably a std::map or a std::set
Don't reinvent the wheel, the STL is full of goodies.
You seem to be rooted in a weakly typed language - but C++ is strongly typed.
You will 'pay' the disadvantage of strong typing almost no matter what you do, but you almost painstakingly avoid the advantage.
Let me start with the field that always says 'fruit' - my suggestion is to make this an enum, like:
enum PlantType { fruit, veggie };
Second, you have a vector that always contain 3 strings, all with the same meaning. this seems to be a job for a struct, like:
struct Post {
PlantType kind;
char firstchar;
string name;
// possibly other characteristics
};
the 'firstchar' is probably premature optimization, but lets keep that for now.
Now you want to add a new Post, to an existing vector of Posts, like:
vector<Post> mainDB;
bool AddOne( const Post& p )
{
for( auto& pp : mainDB )
if( pp.name == p.name )
return false;
mainDB.push_back(p);
return true;
}
Now you can use it like:
if( ! AddOne( Post{ fruit, 'b', "banana" } ) )
cerr << "duplicate entry";
If you need speed (at the cost of memory), switch your mainDB to map, like:
map<string,Post> mainDB;
bool AddOne( const Post& p )
{
if( mainDB.find(p.name) != mainDB.end() )
return false;
mainDB[p.name]=p;
return true;
}
this also makes it easier (and faster) to find and use a specific post, like
cout << "the fruit is called " << mainDB["banana"].name ;
beware that the above will cause a runtime error if the post dont exists
As you can see, firstchar was never used, and could be omitted. std::map
has a hash-function-specialization for string keys, and it will probably be
orders of magnitude faster than anything you or I could whip up by hand.
All of the above assumed inclusion of the correct headers, and
using namespace std;
if you dont like using namespace, prepend std:: to all the right places
hope it helps :)

find values in map of map & return primary map's keys (or alternative)

I'm writing a websocket++ server for a site that's mostly a few pages with article listings like you'd see on any social news site or the questions link on stack; however, I dynamically change the shown articles via faux buttons and sliders as well as update them with websocket pushes.
I need to store, of course, each connection, but I also want the site to remember where each user is on each page. This is an index position of the articles for each page.
If I use (psuedocode): map<sessionid, map<page, index>>, can I find the index directly? If not, what code should I use to achieve this?
If you can, please balance between minimal lines of code & max performance.
multiple sessionids
Sorry, forgot that many people will probably be looking at the same index per page, so it needs to output a set (unless if there's something better) of only sessionids.
So, in pseudocode, it might look like findPageIndex(map<sessionid, map<page, index>>, page, index) and return set<sessionid>.
pages
Every time a session is created, the indexes will default to 0 for each page until a user changes them.
reasoning
The purpose of this format is to update the user whenever another user changes an article through votes, comments, etc. I want that new data immediately sent back to the the client looking at those articles.
So:
User1 updates article1 in the database
server finds all users looking at article1
server updates all users looking at article1
This is my best guess at how stack and other advanced sites do this.
If I understand your question correctly, and provided you are given a sessionId and a page, you could just do a nested search (here I am assuming sessionId, page, and index are all type aliases for int, but that doesn't have to be the case obviously).
This is how you could do it in C++11:
#include <map>
#include <algorithm>
#include <stdexcept>
int find_index(
std::map<int, std::map<int, int>> const& m,
int sessionId,
int pageId)
{
auto i = m.find(sessionId);
if (i != m.end())
{
auto j = i->second.find(pageId);
if (j != i->second.end())
{
return j->first;
}
}
throw std::logic_error("Invalid coordinates");
}
This is how you could use it:
#include <iostream>
int main()
{
std::map<int, std::map<int, int>> m;
m[42] = std::map<int, int>{{1729, 6}};
std::cout << find_index(m, 42, 1729);
}
And here is a live example.
UPDATE:
After the edit, it turned out the requirements where quite different. If I understood correctly, given a page and an index, you want to retrieve all the sessionIds for which there is apage -> index` association in the corresponding inner map. In that case, the algorithm could look like this:
#include <map>
#include <algorithm>
#include <vector>
std::vector<int> find_sessions_by_page_and_index(
std::map<int, std::map<int, int>> const& m,
int page,
int index)
{
std::vector<int> result;
for (auto const& p : m)
{
auto i = p.second.find(page);
if ((i != p.second.end()) && (i->second == index))
{
result.push_back(p.first);
}
}
return result;
}
And this is how you would use it:
#include <iostream>
int main()
{
std::map<int, std::map<int, int>> m;
m[42] = std::map<int, int>{{1729, 6}};
m[24] = std::map<int, int>{{1729, 6}};
m[5] = std::map<int, int>{{1729, 12}};
m[10] = std::map<int, int>{{9271, 6}};
auto v = find_sessions_by_page_and_index(m, 1729, 6);
for (auto x : v)
{
std::cout << x << " ";
}
}
And of course the live example.
typedef map<page, index> page_map;
typedef map<sessionid, page_map> ses_map;
ses_map the_map; // The main map
index index_wanted; // The index you are looking for.
for(ses_map::iterator ses_it = the_map.begin(); ses_it != the_map.end(); ++ses_it) {
for(page_map::iterator page_iter = ses_it->second.begin(); page_iter != ses_it->second.end(); ++page_iter) {
if(index_wanted == page_iter->second) {
// Found it!
}
}
}