What would be the key in case of Objects being inserted into std::set? As for the following example, I am inserting objects of class Classcomp into the std::set. However, I want to find whether an object of Classcomp with a particular 'id = 1' exists in the std::set or not?
#include <iostream>
#include <set>
struct Classcomp {
int val = 0;
int id = 0; ///ID for the object
bool operator() (const Classcomp& lhs, const Classcomp& rhs) const
{return lhs.val<rhs.val;}
};
int main ()
{
Classcomp c1,c2,c3,c4;
c1.val = 92;c1.id = 2;
c2.val = 94;c2.id = 3;
c3.val = 10;c3.id = 1;
std::set<Classcomp,Classcomp> fifth; // class as Compare
fifth.insert(c1);fifth.insert(c2);fifth.insert(c3);
for (auto x: fifth) {std::cout << x.id << " " << x.val << std::endl;}
if (fifth.find()) {std::cout << "Got it";} //What should I pass as arguments to fifth.find()?
return 0;
}
Sets is different from the map by exactly the fact that values of objects inside the set are their keys. If you need to separate a key from the value, you need a key-value container, such as std::map or std::unordered_map.
Clarification
I am not talking about the option of simply iterating over all objects in the set and checking every object to have specified key - which is easily done with std::find. If that's what you want, please clarify.
As your class is sorted by field val you have following options:
iterate over all elements and find one with particular id by std::find or std::find_if
create another container and sort there by id (maybe using std::share_ptr to avoid duplicates)
use boost::multiindex and create index by val as well as id
Related
I'm looking to remove one key - value element from an std::map and keep the value which was stored in this map. Just removing it is not enough, I need the key and value which was stored for other things.
My example is something like this:
std::map<const Class1 *, std::unique_ptr<Class 2>> myMap;
I would like to extract the key and value from the std::map. Simply moving it away is not an option, since this will put the std::map in an incorrect state.
I found the std::extract function, which I can use for an std::set, but not for an std::map. I cannot find any example online showing how I can extract the key and value from this map.
I would like to do something like this:
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.someMethod();
auto value = std::move(keyAndValue.someOtherMethod());
I thought I could use key() and value(), as described in some examples. But this does not work, I get an error.
error C2039: 'value': is not a member of
'std::_Node_handle<std::_Tree_node<std::pair<const
_Kty,_Ty>,std::_Default_allocator_traits<_Alloc>::void_pointer>,_Alloc,std::_Node_handle_map_base,_Kty,_Ty>'
You answered the question by yourself.
I just want to add a complete example. Please see:
#include <iostream>
#include <map>
#include <functional>
#include <memory>
struct Class1
{
void someMethod() const { std::cout << "Some Method from class 1\n";}
};
struct Class2
{
Class2(const int i) : test(i) {}
void someOtherMethod() const { std::cout << "Some other Method from class 2. Index: " << test << '\n';}
int test{0};
};
int main()
{
Class1 cl1_1{}, cl1_2{}, cl1_3{};
std::map<const Class1 *, std::unique_ptr<Class2>> myMap;
// Populate map
myMap[&cl1_1] = std::make_unique<Class2>(1);
myMap[&cl1_2] = std::make_unique<Class2>(2);
myMap[&cl1_3] = std::make_unique<Class2>(3);
// Extract data
auto keyAndValue = myMap.extract(myMap.find(&cl1_1));
// Get key and value
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
// Call methods
key->someMethod();
value->someOtherMethod();
return 0;
}
The answer was suggested by Mark.
For an std::map, I need to use mapped() on the node I get, not value.
Thanks a lot!
So, like this
auto keyAndValue = myMap.extract(myMap.find(instanceOfClass1));
auto key = keyAndValue.key();
auto value = std::move(keyAndValue.mapped());
I am currently struggling coming up with an optimized method for dynamic ordering. I currently have a vector that looks like this in some place of my code
std::vector<std::string> vec {
"optionB",
"optionA",
"optionC"
};
The items in the above vector can be shuffled.The items in this vector are inserted in a specific order so the above order can be different. For simplicity sakes I added the items during declaration.There are about 9 items in the actual case for simplicity I am using only 3 string items.
Now somewhere else in my code I have something like this.
void filter()
{
bool _optionA,_optionB,_optionC
...
//These boolean variables get assigned values
...
...
/*
Todo : I would like to change the ordering of the
following code based on the ordering of items in the
vector. Currently its in the order _optionA _optionB,
_optionC. I would like this ordering to be based
on the order of the strings as in the above vector.
so it should be _optionB,_optionA,_optionC ,
I understand the items in the vector are string
and the following are boolean types
*/
if(_optionA){
}
if(_optionB) {
}
if(_optionC){
}
}
The simplest approach that comes to my mind is
for(auto str : vec)
{
if( (str=="optionA" && _optionA))
{
//This was optionA
}
else if( (str=="optionB" && _optionB)) {
}
else if( (str=="optionC" && _optionC)) {
}
}
I want to know what would be the most optimized way to accomplish the above task ? I am looking for a solution that would avoid iterating through a vector since its in a performance centric piece of code. Is there a way for me to use integrate bitwise operations or something like array indexing to accomplish this task ? Please let me know if something is unclear
It sounds like you want map a string to an actual process. Could you create an interface option class and have instances of options mapped to the string that should cause them to occur? That way you could use the string as a key to get back an Option object and call something like myOption.execute().
The downside to this method is that you need to create a new option class and have it inherit from the interface each time you need a new option.
#Edit: Sorry I think I may have misunderstood the question. But I think the premise still applies you could have a map of string to boolean and just use the string as a key to get back whether the option is toggled on or off.
Assuming you load the vector in on start up, you can sort it at that point to your liking. For example, in alphabetical order. This will mean that you know the order of the vector therefore you can simply reference the vector by index when checking in the your filter function.
Load in data into vector std::vector<string> data = {"optionA", "optionB"};.
Sort using std::sort(data.begin, data.end); or any other sort method of your choice.
Then in you filter function check the vector based on index. if (data.at(1) == "optionA") { }
If I understand your problem correctly, you need to imply order_by on the boolean variables/predicates.
In the below program I will refer your (_optionA, _optionB, _optionC) as predicates even though they are bool, since we can upgrade this problem to work with predicates as well.
Based on the above assumption, I am going ahead with an implementation.
You should pass an ordered_predicates to your filter function.
ordered_predicates is sorted according to your desired criteria.
filter()'s job is just to execute them in the order defined.
auto filter(std::vector<bool> const & ordered_predicates)
-> void
{
for (auto const & condition : ordered_predicates) {
if (condition) {
// ... do your usual stuff here
}
}
}
So how should we go ahead to achieve this ordered_predicates?
We will create a function called order_by that will take an order_by_criteria and a mapping, which will help it in creating ordered_predicates.
With this function, creating ordered_predicates is just a one time cost.
auto order_by(std::vector<std::string> const & order_by_criteria,
std::map<std::string, bool> const & mapping)
-> std::vector<bool>
{
std::vector<bool> ordered_predicates;
for (auto const & item : order_by_criteria)
ordered_predicates.push_back(mapping.at(item));
return ordered_predicates;
}
Where order_by_criteria is your std::vector<std::string> and mapping is just a map which tells which string and predicates are associated.
std::vector<std::string> order_by_criteria { "optionB", "optionA", "optionC" };
std::map<std::string, bool> mapping = { {"optionA", _optionA },
{"optionB", _optionB },
{"optionC", _optionC } };
Here is a complete working program for your reference.
#include <iostream>
#include <map>
#include <vector>
auto order_by(std::vector<std::string> const & order_by_criteria,
std::map<std::string, bool> const & mapping)
-> std::vector<bool>
{
std::vector<bool> ordered_predicates;
for (auto const & item : order_by_criteria)
ordered_predicates.push_back(mapping.at(item));
return ordered_predicates;
}
auto filter(std::vector<bool> const & ordered_predicates)
-> void
{
for (auto const & condition : ordered_predicates) {
if (condition) {
// ... do your usual stuff here
}
}
}
int main()
{
bool _optionA = true, _optionB = false, _optionC = true;
std::vector<std::string> order_by_criteria { "optionB", "optionA", "optionC" };
std::map<std::string, bool> mapping = { {"optionA", _optionA },
{"optionB", _optionB },
{"optionC", _optionC } };
auto ordered_predicates = order_by(order_by_criteria, mapping);
filter(ordered_predicates);
filter(ordered_predicates); // call as many times as you want, with pre-decided order
return 0;
}
If I got the problem correctly, sorting is a way to go. Just sort the vector together with bool flags, using std::vector values as keys, and then simply check bool flags in fixed, lexicographic, order.
Suppose we have a vector {"optB", "optC", "optA"}. After sorting, the indices {0, 1, 2} will rearrange: std::size_t perm[] = {2, 0, 1}. Using this information, that can be precomputed (outside filter(...)), we can rearrange the bool flags:
bool options[N];
// populate options...
bool new_options[N];
for (std::size_t i = 0; i < N; ++i)
new_options[perm[i]] = options[i];
Now we simply check new_options successively:
if (new_options[0]) {
...
}
if (new_options[1]) {
...
}
To precompute perm array use std::map:
std::map<std::string, std::size_t> map;
for (std::size_t i = 0; i < N; ++i)
map.emplace(vec[i], i);
std::size_t perm[N];
auto m = map.begin();
for (std::size_t i = 0; i < N; ++i, ++m)
perm[i] = m->second;
I'm a beginner C++ programmer so there are language constructs that I don't understand which prevents me from understanding map's API (for your reference, here)
More to the point, some questions:
How do I change the internal sorting scheme of map so that, given that we're working with map::<string, ...>, the key values are sorted alphabetically?
More specifically about the map::key_comp, is it a define-and-forget thing where once we define what it means for two elements of the same type to be "unequal (one is less than the other)", then the sorting is done internally and automatically - so all we need to is insert key/value pairs? Or do we have to define equality/ordering and then call the function explicitly to return a boolean to implement ordered insertion?
Here's an example of how you give the sorted map a template argument to use a non-default sort:
std::map<int, int, std::greater<int> > m;
Taken from C++ std::map items in descending order of keys
Also, for a more complex example: how to declare custom sort function on std::map declaration?
[Solved] Solution I'd chosen
Suppose your map was: map<int,vector<int>,compAB> myMap;
Then you need to do 2 things to define "compAB":
Define a comparison function that returns true or false/(or 1 or 0). I haven't tested whether this function can take multiple arguments (although I don't see why it can't so long as it returns a bool. This function should specify how you intend to order two objects of the same type as the key value in your your map.
bool compare(int a, int b)
{
if(a < b) return true;
return false; //Default
}
The API for the map::key_comp object (here indicates that the comparison function needs to return true if the first argument is "less than"/"comes before" the second argument. It should return false otherwise source). Here, I've used a simple criteria for determining "less than": a precedes b if a<b, as the computer evaluates it.
Create a struct whose member consist of just an operator-overload:
struct compAB
{
bool operator()(int i1, int i2){
return compare(i1, i2);
}
};
Then, you can make the declaration: map<int,vector<int>,compAB> myMap; and any calls to insertion() will insert the pairs you've indicated according to the key-ordering scheme you specified
For instance:
#include <iostream>
#include <map>
bool compare_descend(int a, int b)
{
//specifies that when a>b return true -> a FOLLOWS b
if(b < a) return true;
return false;
}
bool compare_ascend(int a, int b)
{
//specifies that when a<b return true -> a PRECEDES b
if(a < b) return true;
return false; //Default
}
struct comp_asc
{
bool operator()(int i1, int i2)
{
return compare_ascend(i1, i2);
}
};
int main()
{
std::cout << "int_map_asc: [key,value]\n";
//Map declaration via class function
std::map<int,std::string,comp_asc> int_map_asc;
//Insert pairs into the map
int_map_asc.insert( std::pair<int,std::string>(0, "Alan") );
int_map_asc.insert( std::pair<int,std::string>(1, "Baye") );
int_map_asc.insert( std::pair<int,std::string>(2, "Carl") );
int_map_asc.insert( std::pair<int,std::string>(3, "David") );
//Iterate & print
std::map<int,std::string,comp_asc>::iterator a_it;
for(a_it=int_map_asc.begin(); a_it!=int_map_asc.end(); a_it++)
std::cout << "[" << a_it->first << "," << a_it->second << "]\n";
std::cout << "\nint_map_desc: [key,value]\n";
//Map declaration via function pointer as compare
bool(*fn_compare_desc)(int,int) = compare_descend; //Create function ptr to compare_descend()
std::map<int,std::string,bool(*)(int,int)> int_map_desc(fn_compare_desc); //fn ptr passed to constructor
//Insert pairs into the map
int_map_desc.insert( std::pair<int,std::string>(0, "Alan") );
int_map_desc.insert( std::pair<int,std::string>(1, "Baye") );
int_map_desc.insert( std::pair<int,std::string>(2, "Carl") );
int_map_desc.insert( std::pair<int,std::string>(3, "David") );
//Ititerate & print
std::map<int,std::string,bool(*)(int,int)>::iterator d_it;
for(d_it=int_map_desc.begin(); d_it!=int_map_desc.end(); d_it++)
std::cout << "[" << d_it->first << "," << d_it->second << "]\n";
return 0;
}
Output:
int_map_asc: [key,value]
[0,Alan]
[1,Baye]
[2,Carl]
[3,David]
int_map_desc: [key,value]
[3,David]
[2,Carl]
[1,Baye]
[0,Alan]
Additional examples here.
Basically, I want to save a set of pointers, which should be sorted by my customized compare function, but the uniqueness should still be determined by the pointer itself.
However:
#include <iostream>
#include <string>
#include <set>
#include <utility>
#include <functional>
using namespace std;
// count, word
typedef pair<int, string> WordFreq;
struct WordFreqPointerCmp
{
bool operator()(const WordFreq* lhs, const WordFreq* rhs) const
{
return lhs->first > rhs->first;
}
};
int main()
{
set<WordFreq*, WordFreqPointerCmp> s;
s.insert(new WordFreq(1, "word1")); // Inserted
s.insert(new WordFreq(1, "word2")); // This is not inserted
s.insert(new WordFreq(3, "word3")); // Inserted
cout << s.size() << endl;
for (set<WordFreq*, WordFreqPointerCmp>::iterator it = s.begin();
it != s.end(); ++it)
{
cout << (*it)->second << ": " << (*it)->first << endl;
}
return 0;
}
/* Output:
2
word3: 3
word1: 1
*/
As you can see that the ordering is correct, but the duplicate testing is wrong. What I am trying to do is:
For ordering, I want to use WordFreqPointerCmp;
For duplicate testing, I want to use the original meaning of raw Pointer comparsion, i.e., the address comparison, which means, even the following set should have two entries in the set;
set<WordFreq*, WordFreqPointerCmp> s;
s.insert(new WordFreq(1, "word1"));
s.insert(new WordFreq(1, "word1"));
I also tried the following, but same result:
template<>
struct greater<WordFreq*>
{
bool operator()(WordFreq* const& lhs, WordFreq* const& rhs) const
{
return lhs->first > rhs->first;
}
};
set<WordFreq*, greater<WordFreq*> > s;
while this post is ancient, I've just faced the same issue, so it may help somebody..
In your code you only handle one value, but what if values are the same? Then set treats it as the same element. The proper solution would be to extend your compare function to give set additional information how to test for duplicates. It can be something arbitrary like comparing strings, for example in your case:
struct WordFreqPointerCmp
{
bool operator()(const WordFreq* lhs, const WordFreq* rhs) const
{
if (lhs->first == rhs->first)
return lhs->second > rhs->second;
else
return lhs->first > rhs->first;
}
};
I am not sure what the problem is. Since you want the first component of your pair to be the key that determines uniqueness, inserting two "WordFreq" with key = 1 should lead to the second evict the first. Results match expectation here.
Update: I guess, I misunderstood something. Since you want duplicate keys, you are probably looking for multimap.
Update 2: To make this work you need to add a step before adding a new object: Iterate over all values of the same key, and kick those out that point to the object being added. Also, I forgot to mention there is multiset which is probably what you'd prefer.
I admit, here is where Java's HashSet with it's separate order and equality tests come in handy. Maybe you can find a C++ version of it.
How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.