referencing an object using auto iterator - c++

unordered_map<char,int> letter;
for (auto it = letter.begin(); it != letter.end() ; it++) {
if (it->second) return false;
}
for (auto it : letter) {
if (it.second) return false;
}
Above, there are 2 iterator loops which I believe output the same thing. I can understand that the it in the first loop points to the object in the unordered_map, so the second variable must be referenced with ->. But I dont understand how the second loop can do .second. Can anyone explain how to 2nd loop works?

The second loop is a range-based for loop. It is not returning an iterator, but is instead returning a copy of the key-value pair (pair<char, int>), so it does not need to ues a -> operator to access the values.
Your range-based for would be equivalent to this, only less verbose, of course.
for (auto it = letter.begin(); it != letter.end() ; it++) {
auto kvp = *it;
if (kvp.second) return false;
}

Related

Finding most common element in a list (C++ STL)?

I have a program where I have to find the most common element in a list of integers. I do this with the program below, but the problem is, I suspect that the erase function messes up with the iterator incrementation in the countRepetition() function. My question is how can I fix the problem or if this is not the issue what is it?
Thanks in advance.
You have a couple issues. First, as you suspected, was the incorrect use of erase. When you erase an iterator it invalidates the iterator. Any use of the iterator afterwards is undefined behavior. Since erase returns the next valid iterator what you can do is restructure the loop like
for (START = l.begin(); START != l.end();) { // do not increment here
if (*START) {
counter++;
START = l.erase(START); // erase and get next
}
else
{
++START; // go to next
}
}
So now at least you loop through the list. Unfortunately you will still have an invalid iterator in main. You pass START from main to countRepetition and when that iterator is erased from the list you then have an invalid iterator. What you need to do is get a new begin iterator from the list each iteration since you are always erasing the first element. That would make your for loop look like
for (START = l.begin(); START != l.end(); START = l.begin()) {
m.push_back(countRepetition(START));
}
Another issue is you just check if the character is not 0. If you are counting repetitions you need to make sure you are checking that the iterator is the same character. I'll leave that for you to implement.
I would also like to point out there is an easier way to do all of this. A std::map lets you build a histogram very easily. Combine that with std::max_element and you could write your entire program as
int main()
{
std::map<char, int> histogram;
while ('0' != (number = getchar()))
++histogram[number]; // add to map, increment count of occurances
auto most_frequent = *std::max_element(histogram.begin(),
histogram.end(),
[](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; }).first;
std::cout << most_frequent;
return 0;
}
Your problem is that you use global variables everywhere.
The global START is changed in two loops, so you only access the first loop once, then it is changed again in the second function and you don't execute the first loop a second time.
Why do you use the global variables? You should not use them but use local variables.
This is probably what you are looking for:
#include <iostream>
#include <list>
#include <vector>
#include <map>
using namespace std;
list <char> l;
map<char, int> ans;
int main()
{
char c;
do{
c = getchar();
l.push_back(c);
}while(c != '0');
for(auto chr: l){
ans[chr]++;
}
char ch;
int mx = 0;
for(auto k: ans){
if(k.second > mx)
{
ch = k.first;
mx = k.second;
}
}
cout<<ch<<" : "<<mx;
}

Converting const auto & to iterator

A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.

Efficient way to delete a value from an unordered_map, when given just the value C++

I have an abstract class called Object and I am using std::unordered_map<int, Object*> objects to contain these Objects within a class called DataSet. Each object has an id associated with it.
Normally, when deleting an object from my unordered_map, I can just do iterator = find(id) and then call erase on that iterator.
This is easy and efficient. The problem is, I have to implement a method to delete an entry/pair by value, rather then by the key (which was my id). This gives me the following prototype:
int DataSet::DeleteObject(Object* object)
What is the most efficient way of accomplishing this though? I'm thinking I can do something like this:
if(object){
for(auto kv : objects) {
if(kv.second == object) {
objects.erase(kv);
}
}
return 1;
}
But it seems very inefficient. So what would be the most efficient way to accomplish this?
Don't perform the lookup twice; erase via iterator:
for (auto it = m.begin(); it != m.end(); )
{
if (it->second == needle) { m.erase(it++); }
else { ++it; }
}
This deletes all occurrences of needle. If you want to erase at most the first occurrence, a simpler loop will do:
for (auto it = m.begin(); it != m.end(); ++it)
{
if (it->second == needle) { m.erase(it); break; }
}
If you want to erase exactly one element, you need to add a check that you found any needles. This can be achieved with find_if, which may also be used as a variation of the previous algorithm:
auto it = std::find_if(m.begin(), m.end(),
[&needle](const auto & p) { return p.second == needle; });
if (it != m.end()) { m.erase(it); }
else { /* no such element! */ }

How can I skip elements in a range-based for loop based on 'index'?

Is there a way to access the iterator (I suppose there's no loop index?) in a C++11 range-based for loop?
Often we need to do something special with the first element of a container and iterate over the remaining elements. So I'm looking for something like the c++11_get_index_of statement in this pseudo-code:
for (auto& elem: container)
{
if (c++11_get_index_of(elem) == 0)
continue;
// do something with remaining elements
}
I'd really like to avoid going back to old-style manual iterator handling code in that scenario.
Often we need to do something special with the first element of a
container and iterate over the remaining elements.
I am surprised to see that nobody has proposed this solution so far:
auto it = std::begin(container);
// do your special stuff here with the first element
++it;
for (auto end=std::end(container); it!=end; ++it) {
// Note that there is no branch inside the loop!
// iterate over the rest of the container
}
It has the big advantage that the branch is moved out of the loop. It makes the loop much simpler and perhaps the compiler can also optimize it better.
If you insist on the range-based for loop, maybe the simplest way to do it is this (there are other, uglier ways):
std::size_t index = 0;
for (auto& elem : container) {
// skip the first element
if (index++ == 0) {
continue;
}
// iterate over the rest of the container
}
However, I would seriously move the branch out of the loop if all you need is to skip the first element.
Boost provides a nice succinct way to do this:
std::vector<int> xs{ 1, 2, 3, 4, 5 };
for (const auto &x : boost::make_iterator_range(xs.begin() + 1, xs.end())) {
std::cout << x << " ";
}
// Prints: 2 3 4 5
You can find make_iterator_range in the boost/range/iterator_range.hpp header.
How about using a simple for loop with iteratos:
for(auto it = container.begin(); it != container.end(); it++)
{
if(it == container.begin())
{
//do stuff for first
}
else
{
//do default stuff
}
}
It's not range based, but it's functional.
In case you may still want to use the range loop:
int counter = 0;
for(auto &data: container)
{
if(counter == 0)
{
//do stuff for first
}
else
{
//do default stuff
}
counter++;
}
No, you can't get the iterator in a range-based for loop (without looking up the element in the container, of course). The iterator is defined by the standard as being named __begin but this is for exposition only. If you need the iterator, it is intended that you use the normal for loop. The reason range-based for loop exists is for those cases where you do not need to care about handling the iteration yourself.
With auto and std::begin and std::end, your for loop should still be very simple:
for (auto it = std::begin(container); it != std::end(container); it++)
When iterating over elements, always prefer to use an algorithm, and use a plain for loop only if none of the algorithms fit.
Picking the right algorithm depends on what you want to do with the elements... which you haven't told us.
If you want to skip the first element, dump example:
if (!container.empty()) {
for_each(++container.begin(), container.end(), [](int val) { cout << val; });
}
There is no way of knowing how far an element is within the container without having an iterator, pointer or an intrusive index. Here's a simple way of doing it:
int index= 0;
for (auto& elem: container)
{
if (index++ == something)
continue;
// do something with remaining elements
}
If you want to skip the first element, another way is to use a std::deque and pop_front the first element. Then you can do your ranged for loop with the container as usual.
When I need to do something like this on a random access container, my habit is to iterate over the indexes.
for( std::size_t i : indexes( container ) ) {
if (i==0) continue;
auto&& e = container[i];
// code
}
the only tricky part is writing indexes, which returns a range of what boost calls counting iterators. Creating a basic iterable range from iterators is easy: either use boost's range concept, or roll your own.
A basic range for an arbitrary iterator type is:
template<typename Iterator>
struct Range {
Iterator b; Iterator e;
Range( Iterator b_, Iterator e_ ):b(b_), e(e_) {};
Iterator begin() const { return b; }
Iterator end() const { return e; }
};
which you can gussy up a bunch, but that is the core.
I would try to avoid using iterators, because the idea of a range-based for loop is to get rid of them. As of C++20, to skip the first element in your container, I would take one of the following approaches. I also include, for the sake of completeness, how to handle the first element separately:
Handling the first element outside the loop
You can use container.front() which exists for all sequence containers to access the first element. However, you must make sure that the container is not empty to avoid a segmentation fault. Then, to skip the first element (or more) in the loop, you can use the range adapter std::views::drop from the Ranges library. All together it looks as follows:
std::vector<int> container { 1, 2, 3 };
if(!container.empty()) {
// do something with first element
std::cout << "First element: " << container.front() << std::endl;
}
for (auto& elem : container | std::views::drop(1)) {
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
Instead of container.front() you can also use another range-based for loop together with the range adapter std::views::take(1). The advantage of take() and drop() is that they work safely even if their arguments exceed the count of elements in your container.
Handling the first element inside the loop
You can use an init-statement in a range-based for loop to define a Boolean flag (or even a counter). This way, the flag is visible only within the scope of the loop. You can use the flag inside the loop as follows:
std::vector<int> container { 1, 2, 3 };
for(bool isFirst(true); auto& elem : container) {
if(isFirst) {
// do something with first element
std::cout << "First element: " << elem << std::endl;
isFirst = false;
continue;
}
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
Output for both approaches shown:
First element: 1
Remaining element: 2
Remaining element: 3
Code on Wandbox

C++ find in vector

I am trying to do function that would return postition where occurrence was found in vector. But my return value is always 0 and I'm sure that there is a match.
Here is the code:
int findInItemvector(vector<Item> vec, string name)
{
for(vector<Item>::iterator it = vec.begin(); it < vec.end(); it++)
{
if(it->getName() == name)
{
return it - vec.begin();
break;
}
else
{
return 0;
}
}
}
When your first element does not match, the else branch executes return, which leaves the function and rest of your loop is not executed. You want something like:
int findInItemvector(vector<Item> vec, string name)
{
for(vector<Item>::iterator it = vec.begin(); it < vec.end(); it++)
{
if(it->getName() == name)
{
return it - vec.begin();
}
}
return 0;
}
However, since first item can also be match (in which case it - vec.begin() == 0), I suggest you to use other guard value, such as -1 (which can never be valid vector index).
Your 'else' clause is the problem: if the first item isn't a match, the 'else' kicks in, and your 'return 0' statement breaks you out of the 'for' loop without trying any of the other elements in the vector.
Try getting rid of the 'else', and moving the 'return 0' to after the end of the 'for' loop.
Your function will always return 0 because of the way it's currently written. If a match is found in the first item of the vector the result of the subtraction is, of course, zero. If it doesn't match the else part executes and returns 0, and the function exits.
The function should probably look like this
int findInItemvector(const vector<Item>& vec, string name)
{
for(vector<Item>::const_iterator it = vec.begin(); it < vec.end(); it++)
{
if(it->getName() == name)
{
return it - vec.begin();
}
}
return -1;
}
Notice that I'm passing the vector by constant reference instead of by value, this will prevent an unnecessary copy of the vector from being made when this function is called.
Also, you should indicate failure using -1, not 0, since the latter is a legitimate result if the string you're looking for is in the first element of the vector.