I have a std::set<Foo>, and I'd like to update some value of
an existing element therein. Note that the value I'm updating does not change the order in the set:
#include <iostream>
#include <set>
#include <utility>
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
typedef std::set<Foo> Set;
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = p.second;
if (alreadyThere)
p.first->val += f.val; // error: assignment of data-member
// ‘Foo::val’ in read-only structure
}
int main(int argc, char** argv){
Set s;
update(s, Foo(1, 10));
update(s, Foo(1, 5));
// Now there should be one Foo object with val==15 in the set.
return 0;
}
Is there any concise way to do this? Or do I have to check if the element is already there, and if so, remove it, add the value and re-insert?
Since val is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
Don't try to solve this problem by working around the const-ness of items in a set. Instead, why not use map, which already expresses the key-value relationship you are modeling and provides easy ways to update existing elements.
Make val mutable as:
mutable int val;
Now you can change/modify/mutate val even if foo is const:
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
By the way, from your code, you seem to think that if p.second is true, then the value already existed in the set, and therefore you update the associated value. I think, you got it wrong. It is in fact other way round. The doc at cpluscplus says,
The pair::second element in the pair is set to true if a new element was inserted or false if an element with the same value existed.
which is correct, in my opinion.
However, if you use std::map, your solution would be straightforward:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
m[value.first] += value.second;
}
What does this code do? m[value.first] creates a new entry if the key doesn't exist in the map, and value of the new entry is default value of int which is zero. So it adds value.second to zero. Or else if the key exists, then it simply adds value.second to it. That is, the above code is equivalent to this:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
std::map<int,int>::iterator it = m.find(value);
if ( it != m.end()) //found or not?
it.second += value; //add if found
else
{
m.insert(value); //insert if not found
}
}
But this is too much, isn't it? It's performance is not good. The earlier one is more concise and very performant.
If you know what you're doing (the set elements are not const per se and you're not changing members involved in comparison), then you can just cast away const-ness:
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = !p.second;
if (alreadyThere)
{
Foo & item = const_cast<Foo&>(*p.first);
item.val += f.val;
}
}
Instead of separate hint, use the erase's return value for the next iterator position
bool alreadyThere = !p.second;
if (alreadyThere)
{
auto nit = s.erase(p.first);
s.insert(nit, f);
}
you can use MAP witch has very fast access to your element if you have KEY . in this case i think using MAP would be better way to achieve fastest speed . STD::MAP
Related
I have a std::set<Foo>, and I'd like to update some value of
an existing element therein. Note that the value I'm updating does not change the order in the set:
#include <iostream>
#include <set>
#include <utility>
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
typedef std::set<Foo> Set;
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = p.second;
if (alreadyThere)
p.first->val += f.val; // error: assignment of data-member
// ‘Foo::val’ in read-only structure
}
int main(int argc, char** argv){
Set s;
update(s, Foo(1, 10));
update(s, Foo(1, 5));
// Now there should be one Foo object with val==15 in the set.
return 0;
}
Is there any concise way to do this? Or do I have to check if the element is already there, and if so, remove it, add the value and re-insert?
Since val is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
Don't try to solve this problem by working around the const-ness of items in a set. Instead, why not use map, which already expresses the key-value relationship you are modeling and provides easy ways to update existing elements.
Make val mutable as:
mutable int val;
Now you can change/modify/mutate val even if foo is const:
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
By the way, from your code, you seem to think that if p.second is true, then the value already existed in the set, and therefore you update the associated value. I think, you got it wrong. It is in fact other way round. The doc at cpluscplus says,
The pair::second element in the pair is set to true if a new element was inserted or false if an element with the same value existed.
which is correct, in my opinion.
However, if you use std::map, your solution would be straightforward:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
m[value.first] += value.second;
}
What does this code do? m[value.first] creates a new entry if the key doesn't exist in the map, and value of the new entry is default value of int which is zero. So it adds value.second to zero. Or else if the key exists, then it simply adds value.second to it. That is, the above code is equivalent to this:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
std::map<int,int>::iterator it = m.find(value);
if ( it != m.end()) //found or not?
it.second += value; //add if found
else
{
m.insert(value); //insert if not found
}
}
But this is too much, isn't it? It's performance is not good. The earlier one is more concise and very performant.
If you know what you're doing (the set elements are not const per se and you're not changing members involved in comparison), then you can just cast away const-ness:
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = !p.second;
if (alreadyThere)
{
Foo & item = const_cast<Foo&>(*p.first);
item.val += f.val;
}
}
you can use MAP witch has very fast access to your element if you have KEY . in this case i think using MAP would be better way to achieve fastest speed . STD::MAP
I have a std::set<Foo>, and I'd like to update some value of
an existing element therein. Note that the value I'm updating does not change the order in the set:
#include <iostream>
#include <set>
#include <utility>
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
typedef std::set<Foo> Set;
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = p.second;
if (alreadyThere)
p.first->val += f.val; // error: assignment of data-member
// ‘Foo::val’ in read-only structure
}
int main(int argc, char** argv){
Set s;
update(s, Foo(1, 10));
update(s, Foo(1, 5));
// Now there should be one Foo object with val==15 in the set.
return 0;
}
Is there any concise way to do this? Or do I have to check if the element is already there, and if so, remove it, add the value and re-insert?
Since val is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
Don't try to solve this problem by working around the const-ness of items in a set. Instead, why not use map, which already expresses the key-value relationship you are modeling and provides easy ways to update existing elements.
Make val mutable as:
mutable int val;
Now you can change/modify/mutate val even if foo is const:
void f(const Foo & foo)
{
foo.val = 10; //ok
foo.id = 11; //compilation error - id is not mutable.
}
By the way, from your code, you seem to think that if p.second is true, then the value already existed in the set, and therefore you update the associated value. I think, you got it wrong. It is in fact other way round. The doc at cpluscplus says,
The pair::second element in the pair is set to true if a new element was inserted or false if an element with the same value existed.
which is correct, in my opinion.
However, if you use std::map, your solution would be straightforward:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
m[value.first] += value.second;
}
What does this code do? m[value.first] creates a new entry if the key doesn't exist in the map, and value of the new entry is default value of int which is zero. So it adds value.second to zero. Or else if the key exists, then it simply adds value.second to it. That is, the above code is equivalent to this:
void update(std::map<int,int> & m, std::pair<int,int> value)
{
std::map<int,int>::iterator it = m.find(value);
if ( it != m.end()) //found or not?
it.second += value; //add if found
else
{
m.insert(value); //insert if not found
}
}
But this is too much, isn't it? It's performance is not good. The earlier one is more concise and very performant.
If you know what you're doing (the set elements are not const per se and you're not changing members involved in comparison), then you can just cast away const-ness:
void update(Set& s, Foo f) {
std::pair<Set::iterator, bool> p = s.insert(f);
bool alreadyThere = !p.second;
if (alreadyThere)
{
Foo & item = const_cast<Foo&>(*p.first);
item.val += f.val;
}
}
you can use MAP witch has very fast access to your element if you have KEY . in this case i think using MAP would be better way to achieve fastest speed . STD::MAP
Quite often I have two variables foo1 and foo2 which are numeric types. They represent the bounds of something.
A user supplies values for them, but like a recalcitrant musician, not necessarily in the correct order!
So my code is littered with code like
if (foo2 < foo1){
std::swap(foo2, foo1);
}
Of course, this is an idiomatic sort with two elements not necessarily contiguous in memory. Which makes me wonder: is there a STL one-liner for this?
I suggest to take a step back and let the type system do the job for you: introduce a type like Bounds (or Interval) which takes care of the issue. Something like
template <typename T>
class Interval {
public:
Interval( T start, T end ) : m_start( start ), m_end( end ) {
if ( m_start > m_end ) {
std::swap( m_start, m_end );
}
}
const T &start() const { return m_start; }
const T &end() const { return m_end; }
private:
T m_start, m_end;
};
This not only centralizes the swap-to-sort code, it also helps asserting the correct order very early on so that you don't pass around two elements all the time, which means that you don't even need to check the order so often in the first place.
An alternative approach to avoid the issue is to express the boundaries as a pair of 'start value' and 'length' where the 'length' is an unsigned value.
No, but when you notice you wrote the same code twice it's time to write a function for it:
template<typename T, typename P = std::less<T>>
void swap_if(T& a, T& b, P p = P()) {
if (p(a, b)) {
using std::swap;
swap(a, b);
}
}
std::minmax returns pair of smallest and largest element. Which you can use with std::tie.
#include <algorithm>
#include <tuple>
#include <iostream>
int main()
{
int a = 7;
int b = 5;
std::tie(a, b) = std::minmax({a,b});
std::cout << a << " " << b; // output: 5 7
}
Note that this isn't the same as the if(a < b) std::swap(a,b); version. For example this doesn't work with move-only elements.
if the data type of your value that you're going to compare is not already in c++. You need to overload the comparison operators.
For example, if you want to compare foo1 and foo2
template <class T>
class Foo {
private:
int value; // value
public:
int GetValue() const {
return value;
}
};
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.GetValue() < rhs.GetValue());
}
If your value is some type of int, or double. Then you can use the std::list<>::sort member function.
For example:
std::list<int> integer_list;
int_list.push_back(1);
int_list.push_back(8);
int_list.push_back(9);
int_list.push_back(7);
int_list.sort();
for(std::list<int>::iterator list_iter = int_list.begin(); list_iter != int_list.end(); list_iter++)
{
std::cout<<*list_iter<<endl;
}
Consider this situation:
void doSmth1(std::map<int,int> const& m);
void doSmth2(std::map<int,int> const& m) {
std::map<int,int> m2 = m;
m2[42] = 47;
doSmth1(m2);
}
The idea is that doSmth2 will call doSmth1 and forward the map it received from its caller. However, it has to add one additional key-value pair (or override it if it is already there). I would like to avoid copying the whole thing just to pass an additional value to doSmth1.
You can't do that with the standard map. But if your problem is that specific, you might consider passing the new element separately:
void doSmth1(std::map<int, int> const & m, int newkey, int newvalue);
void doSmth2(std::map<int, int> const & m)
{
doSmth1(m, 42, 47);
}
Update: If you really just want one map, and copying the map is out of the question, then here's how you can implement #arrowdodger's suggestion to make a temporary modification to the original map:
void doSmth2(std::map<int, int> & m)
{
auto it = m.find(42);
if (it == m.end())
{
m.insert(std::make_pair(42, 49));
doSmth1(m);
m.erase(42);
}
else
{
auto original = it->second;
it->second = 49;
doSmth1(m);
it->second = original;
}
}
I'd like to use std::find_if to search for the first element in my map that has a certain value in a specific element of its value structure. I'm a little confused though. I think I need to use bind1st or bind2nd, but I'm not positive that's the right way to go.
Here's some pseudo-code:
struct ValueType { int x, int y, int z };
std::map<int, ValueType> myMap;
... {populate map}
std::map<int, ValueType>::iterator pos = std::find_if(myMap.begin(), myMap.end(), <?>);
So, let's say that I wanted to find the first element of the map where the .x member of the ValueType was equal to a certain integer value (which can change each call).
What would be the best way to write a function or function object to achieve this? I understand that the <?> has to be a unary predicate which makes me think I'll need bind1st or bind2nd to provide the integer value I'm checking for, but I'm not sure how to go about it. It's been way too long since I looked at this stuff! >.<
You can use a lambda function
int val = ...;
auto it = std::find_if(myMap.begin(), myMap.end(),
[val](const std::pair<int, ValueType> & t) -> bool {
return t.second.x == val;
}
);
But as Kirill V. Lyadvinsky answer suggests the "first" element may not be what you expect.
Elements in the map are not sorted by value, they are sorted according to the key. So the phrase "the first element" has not much sense.
To find some element (not the first) that has x equal to some value you can write the functor as follows:
struct check_x
{
check_x( int x ) : x_(x) {}
bool operator()( const std::pair<int, ValueType>& v ) const
{
return v.second.x == x_;
}
private:
int x_;
};
Then use it as follows:
// find any element where x equal to 10
std::find_if( myMap.begin(), myMap.end(), check_x(10) );
For the lazy, use a C++17 auto lambda, then you don't need to be verbose with the type.
const auto it = std::find_if(myMap.begin(), myMap.end(), [&val](const auto &it) {
return it.second.x == val; // Comparing with the object
}
);
Building on all the answers above I cheat by using decltype with C++11 semantics.
auto beg_ = myMap.begin();
auto end_ = myMap.end();
auto it = find_if(beg_, end_,
[&some_val](decltype(*beg_) & vt) {
return vt.second == some_val;});
if (end_ != it) {
auto key_found = (*it).first;
} else {
// throw error not found.
}
struct Pred
{
Pred(int x) : x_(x) { }
bool operator()(const std::pair<int, ValueType>& p)
{
return (x_ == p.second.x);
}
private:
int x_;
};
... = std::find_if(myMap.begin(), myMap.end(), Pred(NUMBER));
This doesn't have anything to do with std::bind1st or std::bind2nd. First of all, you have to keep in mind that the elements of a map are key-value pairs, in your case std::pair<int,ValueType>. Then you just need a predicate that compares the x member of the second member of yuch a pair against a specific value:
struct XEquals : std::unary_function<std::pair<int,ValueType>,bool>
{
XEquals(int _x)
: x(_x) {}
bool operator()(const std::pair<int,ValueType> &v) const
{ return p.second.x == x; }
int x;
};
If you want to search also in values then may be better to use Boost Bimap in order not to be slow?
using Boost.Bind and Boost.Lambda:
...
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
...
typedef std::map<int, ValueType> MapType;
...
MapType::iterator pos = std::find_if(myMap.begin(), myMap.end(),
boost::bind(&ValueType::y, boost::bind(&MapType::iterator::value_type::second, _1)) == magic_number);