checking if key exist in map then updating value - c++

In my project I want to insert keys to a map. All new keys should get the value 1.0, but existing keys should be incremented by 1.
Here's the code
vector <string> pairs;
map<string, float> two;
map <string, float>::iterator it;
string a = "a";
string b = "b";
string c = "a";
pairs.push_back(a);
pairs.push_back(b);
pairs.push_back(c);
for(int i=0; i<pairs.size(); i++)
{
it = two.find(string(pairs[i]) );
if(i==0)
{
two[string(pairs[i])]=1.0;
}
else if ( it == two.end() )
{
it->second = it->second + 1.0;
//after this line ^,my compiler stop working
}
else
{
two[string(pairs[i])]=1.0;
}
}
After this, the object should be
a 2
b 1
How can I do so.

The easiest and most efficient solution is:
for (auto const& s : pairs) two[s] += 1.0;
This works because the [] operator on maps automatically creates an entry if the key isn't present, using the default value constructor. For floats, the default constructor produces a 0.0.
Since [] returns a reference, no additional lookup will be done in order to increment the value.

else if ( it == two.end() )
{
it->second = it->second + 1.0;
Above line of code need to correct as follows
else if ( it != two.end() )
^^^
{
it->second = it->second + 1.0;
More than that:
it = two.find(string(pairs[i]) );
Above line can rewrite as follows
it = two.find(pairs[i] );

The STL was designed to do this efficiently, and it pays to see how.
But first, note that in your code, the lines
two.find(string(pairs[i]) );
two[string(pairs[i])]=1.0;
perform two lookups, which is a bit of a waste.
If you look at the signature for map::insert, you can see that the return value is std::pair<iterator, bool>. The second is a boolean indicating whether the element was actually inserted. The first is an iterator to either the previous element (if it existed, in which case it was not overwritten), or to the new element.
So, the way to do it efficiently is to write
auto ins = two.insert(make_pair(pairs[i], 0));
ins.first->second += 1;

There should be it != two.end() instead of it == two.end()
I think also the 1st condition (i==0) checking can be skipped

Related

C++ Simplify loop over map and extending/overwriting of vector

Given
std::vector<int> vec1 of size s_vec and capacity c.
std::vector<int> vec2.
std::map<int, int> m of size s_m >= s_vec.
std::unordered_set<int> flags.
bool flag = False
I want to copy as many values of m (in order) into vec1 (overwriting previous values) without exceeding the capacity c. If any values remain I want to push those values to the end of vec2. For each of these, values I want to check if they are in flags. If they are, I'd like to set flag to true.
This is how I currently, achieve this:
int i = 0;
for (auto const& e : m) {
if(i < c) {
if(i == vec1.size()) {
vec1.push_back(e.second);
} else {
vec1.at(i) = e.second;
}
} else {
vec2.push_back(e.second);
if(flags.count(e.second)){
flag = true;
}
}
}
I am new to C++ coming from python and R. Therefore, I assume that this can be simplified quite a bit (with iterators?). What can I do to improve the code here?
Your code must increment i at the end of each loop for it to work.
If you can use c++20 and its ranges, I would probably rewrite it completely, to something like:
using namespace std::views; // for simplicity here
std::ranges::copy(m | take(c) | values, vec1.begin());
std::ranges::copy(m | drop(c) | values, std::back_inserter(vec2));
flag = std::ranges::any_of(vec2, [&flags](int i){return flags.contains(i);});
The beauty of this, is that it matches your requirements much better.
The first lines does: "I want to copy as many values of m (in order) into vec1 (overwriting previous values) without exceeding the capacity c."
The second line does: "If any values remain I want to push those values to the end of vec2."
The third line does: "For each of these, values I want to check if they are in flags. If they are, I'd like to set flag to true."
Building on the comments of #PaulMcKenzie and the answers provided by #Nelfeal and #cptFracassa, this is what I ended up with.
size_t new_size = std::min(vec1.capacity(), m.size());
vec1.resize(new_size);
std::transform(m.begin(),
std::next(m.begin(), new_size),
vec1.begin(),
[](std::pair<int, int> p) { return p.second; });
std::transform(std::next(m.begin(), new_size),
m.end(),
std::back_inserter(vec2),
[&flags, &flag](std::pair<int, int> p) {
if(flags.count(p.second)) {
flag = true;
}
return p.second;
});
In the first part, instead of doing either push_back or assignment to at, you can just clear the vector and push_back everything. clear does not change the capacity.
Your loop is doing two different things, one after the other (and by the way, I assume you forgot to increment i). You should split it into two loops.
With all that, your code becomes:
vec1.clear();
auto it = m.begin();
for (int i = 0; i < c; ++i) {
vec1.push_back(it->second);
++it;
}
while (it != m.end()) {
vec2.push_back(it->second);
if(flags.count(it->second)){
flag = true;
}
++it;
}
At this point, you can also use standard algorithms (std::copy, std::transform as mentioned in the comments).

How to modify every value in unordered map in c++

I have a unordered map (umap) in C++ :
unordered_map<int, bool> dTimeResetUmap;
I am setting its value like:
dTimeResetUmap[person.object_id] = true;
person.object_id can be 0, 1, 2, 3 any int number. At certain point in code, I have to modify the al the values in it (basically have to make all the values as false) which I am doing like below:
int size_of_dTimeResetUmap = dTimeResetUmap.size();
for (int i = 0; i <= size_of_dTimeResetUmap; i++)
{
dTimeResetUmap[i] = false;
}
But it seems not to be working for some value. After a long run of code, there are few values inside dTimeResetUmap which remains true instead of false. What can be the reason. Is it not a good way of updating values. Please help Thanks.
Use C++ iterations to visit each element of map:
for (auto & element : dTimeResetUmap)
{
element.second = false;
}
If you use the indexing operator [] to access a value in the map, and the key isn't in the map, then a new key-value pair will be created, with a default "zero" value.
For a bool value, this "zero" will be equal to false.
So the simplest way to set all elements to false is to just remove all elements as then all access to the non-existing keys will create false values:
dTimeResetUmap.clear();
You can use STL iterators;
for (auto it = umap.begin(); it != umap.end(); it++) {
(*it).second = false;
}

Finding position of the element in array

How can I find position of the element inside an array? I have following piece of code where I need to test if the element is at some position, however it is not working as expected. I need your help about it.
string knockemdead[4], bashemup[4], street[4], newyork[9999];
string car;
if (knockemdead[i] == car)
{
if (knockemdead[i].find(1)){ // tried knockemdead[i] = knockemdead[1] and knockemdead[i].at(1) but all it did was nothing
fare = 10;
}
else if (knockemdead[i].find(2))
{
fare = 15;
}
else if (knockemdead[i].at(3) || knockemdead[i].at(4))
{
fare = 25;
}
if cont is some form of container of T, and obj is an object of T,
and T implements ==, then:
auto iter = find( begin(cont), end(cont), obj );
will return either an iterator to the object (or something that compares equal to it),
or end() if no such object exists in the container.
if the container is random-access (vector,array etc), then:
auto idx = iter - begin(cont);
will return the index of the found object
find is declared in <algorithm>, and namespace std is assumed to be accessible
A different solution would be to do it 'manually'
int idx;
for(idx=0; idx<SZ; ++idx)
if( cont[idx] == obj ) break;
you need to put the size of the container in SZ prior.
idx will have the value SZ if no object was found, or the index if it was
You can make a method that finds the position.
Example
int findElementPositionInArray(TYPE[] array , TYPE elementValue){
for(int i=0 ; i<array.length ; i++){
if(array[i]==elementValue){
return i;
}
//let's also treat the case in which the element is not found in the array
//this way we can test the output (we know that if this method returns -1
//the element is not in the array
return -1;
}
}
So just replace TYPE with your desired type (String for your case as I can see) and call this method .
For more complex data types (example your own class that might contain multiple primitive typess) be sure to properly override the "==" operator.
Might contain errors , I don't have an IDE to test this right now.Hope it helps

Checking if reducing iterator points to a valid element

I need to know if I can reduce the iterator and have a valid object. The below errors out because I reduce the iterator by 1 which doesn't exist. How can I know that so I don't get the error?
ticks.push_front(Tick(Vec3(0, 0, 5), 0));
ticks.push_front(Tick(Vec3(0, 0, 8), 100));
ticks.push_front(Tick(Vec3(0, 0, 10), 200));
bool found = false;
list<Tick, allocator<Tick>>::iterator iter;
for (iter = ticks.begin(); iter != ticks.end(); ++iter)
{
Tick t = (*iter);
if (214>= t.timestamp)
{
prior = t;
if (--iter != ticks.end())
{
next = (*--iter);
found = true;
break;
}
}
}
I'm trying to find the entries directly "above" and directly "below" the value 214 in the list. If only 1 exists then I don't care. I need above and below to exist.
After your edits to the question, I think I can write a better answer than what I had before.
First, write a comparison function for Ticks that uses their timestamps:
bool CompareTicks(const Tick& l, const Tick& r)
{
return l.timestamp < r.timestamp;
}
Now use the function with std::upper_bound:
// Get an iterator pointing to the first element in ticks that is > 214
// I'm assuming the second parameter to Tick's ctor is the timestamp
auto itAbove = std::upper_bound(ticks.begin(), ticks.end(), Tick(Vec3(0, 0, 0), 214), CompareTicks);
if(itAbove == ticks.end())
; // there is nothing in ticks > 214. I don't know what you want to do in this case.
This will give you the first element in ticks that is > 214. Next, you can use lower_bound to find the first element that is >= 214:
// get an iterator pointing to the first element in ticks that is >= 214
// I'm assuming the second parameter to Tick's ctor is the timestamp
auto itBelow = std::lower_bound(ticks.begin(), ticks.end(), Tick(Vec3(0, 0, 0), 214), CompareTicks);
You have to do one extra step with itBelow now to get the first element before 214, taking care not to go past the beginning of the list:
if(itBelow == ticks.begin())
; // there is nothing in ticks < 214. I don't know what you want to do in this case.
else
--itBelow;
Now, assuming you didn't hit any of the error cases, itAbove is pointing to the first element > 214, and itBelow is pointing to the last element < 214.
This assumes your Ticks are in order by timestamp, which seems to be the case. Note also that this technique will work even if there are multiple 214s in the list. Finally, you said the list is short so it's not really worth worrying about time complexity, but this technique could get you logarithmic performance if you also replaced the list with a vector, as opposed to linear for iterative approaches.
The answer to your core question is simple. Don't increment if you are at the end. Don't decrement if you are at the start.
Before incrementing, check.
if ( iter == ticks.end() )
Before decrementig, check.
if ( iter == ticks.begin() )
Your particular example
Looking at what you are trying to accomplish, I suspect you meant to use:
if (iter != ticks.begin())
instead of
if (--iter != ticks.end())
Update
It seems you are relying on the contents of your list being sorted by timestamp.
After your comment, I think what you need is:
if (214>= t.timestamp)
{
prior = t;
if (++iter != ticks.end())
{
next = *iter;
if ( 214 <= next.timestep )
{
found = true;
break;
}
}
}
Update 2
I agree with the comment made by #crashmstr. Your logic can be:
if (214 <= t.timestamp)
{
next = t;
if ( iter != ticks.begin())
{
prior = *--(iter);
found = true;
break;
}
}
I think you can do what you want with std::adjacent_find from the standard library <algorithm>. By default std::adjacent_find looks for two consecutive identical elements but you can provide your own function to define the relationship you are interested in.
Here's a simplified example:
#include <algorithm>
#include <iostream>
#include <list>
struct matcher
{
matcher(int value) : target(value) {}
bool operator()(int lo, int hi) const {
return (lo < target) && (target < hi);
}
int target;
};
int main()
{
std::list<int> ticks = { 0, 100, 200, 300 };
auto it = std::adjacent_find(ticks.begin(), ticks.end(), matcher(214));
if (it != ticks.end()) {
std::cout << *it << ' ' << *std::next(it) << '\n';
} else {
std::cout << "not found\n";
}
}
This outputs 200 300, the two "surrounding" values it found.

Increment an iterator c++

My problem is as follows: I use an iterator, and I want to compare each element to the next element. Prototype looks like below, how can I increase the iterator to be able to compare?
Also, how can I set a proper condition for this to happen? I mean how to point on the last element, not on the next after the last like with end() function:
std::vector<T>::const_iterator it;
std::vector<T>::const_iterator it2;
for (it = set.begin(), it != set.end(); it++)
{
// some things happen
if ( final == it )
{
if ( it != set.end()-1 ) // how to write properly condition?
{
it2 = it + 1; //how to assign the next here?
if (...)//some condition
{
if ( it->func1() - it2->func1()) < 20 ) //actual comparison of two consecutive element values
// do something
}
}
}
}
In C++11 use the functions std::next() and std::prev().
Your code could become:
// before
it != std::set.end()-1
// after
it != std::prev(set.end())
and
// before
it2 = it + 1;
// after
it2 = std::next(it);
That is true also for non-vector containers, such as map,set or others.
NOTE: after std::next(it), "it" iterator remains unmodified!
NOTE 2: Use it2 = std::next(it,n); to increment as much as you need.
You can use adjacent_find to solve that. You should use the second form of that function (with predicate) and pass to the predicate your some things happen and some condition in c-tor
auto found = std::adjacent_find( set.begin(), set.end(),
[some_comdition]( const T & left, const T & right ) {
if ( some_comdition ) {
if ( left.func1() - right.func1() < 20 ) {
do_smth();
// return true; if there's no need to continue
}
}
return false;
}
);
Based on the fact that it++ is acceptable, we should define a new iterator called itplusone, which is initialized as itplusone = ++it. In this way, you can safely use the meaning of an iterator pointing to the next item of it. Also clearly, the range of iterator of itplusone bounded by terms itplusone != set.end(). I use this method to compute the total weight of a path, which is defined as a list object.
In the for loop, you use it++ which means it = it + 1, which is perfectly ok. So this one will be fine also it2 = it + 1. it2 will be pointing to the next value.
In the for loop again, you use it != set.end(), which is again perfectly ok. So you can also it + 1 < set.end(), just like you did in your code.
I don't see anything wrong in your code, just wanted to explain.
somewhat late, just discovered it, but like mentioned above, ++ iterator works fine.
vector<string> P
auto itA = begin(P);
while(itA != end(P))
{
if(itA != end(P))
{
++itA; //
}
}