Checking if a key exists in an unordered_map and incrementing its value

I'm trying to check if an unordered_map contains a particular key, and if this is true, then increment the key's associated value by 1. However, I seem to be having some problems due to my lack of knowledge on how to use such objects, and was hoping I could get some advice on the subject.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <vector>
#include <unordered_map>
#include <iostream>

int main() {
	std::vector<int> nums = { 1,2,3,4,5,5,5,5,5,43,3,1,4,1,4,5,4,4,5 };
	std::unordered_map<int, int> numbers_seen;
	int len = nums.size();

	for (int i = 0; i < len; i++) {
		if (numbers_seen[nums[i]] == true) {
			numbers_seen[nums[i]] += 1;
		}
		else {
			numbers_seen[nums[i]] = 1;
		}
	}

	std::cout << numbers_seen[1] << std::endl;
        std::cout << numbers_seen[4] << std::endl;
        std::cout << numbers_seen[5] << std::endl;
	std::cin.get();
	return 0;
}


I feel that my use of if (numbers_seen[nums[i]] == true) to check if a key exists is incorrect, and similarly, I think that the use of numbers_seen[nums[i]] += 1 to increment the key's value by 1 is also incorrect (since testing shows that none of the key values are incremented above 1 when using the test vector). Any help would be much appreciated.
Last edited on
1
2
3
4
5
auto it = numbers_seen.find(nums[i]);
if (it != numbers_seen.end())
    it->second++;
else
    numbers_seen[nums[i]] = 1;
Note that it is operator[] that inserts the element into the map if the key doesn't exist, not operator=. It just so happens that the value is automatically initialized to 0, so you don't actually need to do anything special when you find a new number that you have never seen before.

1
2
3
for (int i = 0; i < len; i++) {
	numbers_seen[nums[i]]++; 
}
...and to merely access unorder_map elements there’s the method at(), which doesn’t insert and throws if key is not found:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <unordered_map>
#include <vector>

int main() {
    std::vector<int> nums {
         1,  2,  3,  4,  5,
         5,  5,  5,  5, 43,
         3,  1,  4,  1,  4,
         5,  4,  4,  5
    };
    std::unordered_map<int, int> numbers_seen;
    for(const int& e : nums) { ++numbers_seen[e]; }

    for(int i{}; i < 7; ++i) {
        std::cout << i << " appears ";
        try {
            std::cout << numbers_seen.at(i);
        } catch (std::out_of_range& e) {
            std::cout << 0;
        }
        std::cout << " times in 'nums'.\n";
    }

    return 0;
}


Output:
0 appears 0 times in 'nums'.
1 appears 3 times in 'nums'.
2 appears 1 times in 'nums'.
3 appears 2 times in 'nums'.
4 appears 5 times in 'nums'.
5 appears 7 times in 'nums'.
6 appears 0 times in 'nums'.

testing shows that none of the key values are incremented above 1

This caught my eye. Shouldn't the values get incremented after they are set? I added some code to test:
1
2
3
4
5
6
7
8
                if (numbers_seen[nums[i]] == true) {
                    std::cout << "saw " << nums[i] << '\n';
                        numbers_seen[nums[i]] += 1;
                }
                else {
                    std::cout << "didn't see " << nums[i] << '\n';
                        numbers_seen[nums[i]] = 1;
                }

Here's the result:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
didn't see 1
didn't see 2
didn't see 3
didn't see 4
didn't see 5
saw 5
didn't see 5
saw 5
didn't see 5
didn't see 43
saw 3
saw 1
saw 4
didn't see 1
didn't see 4
saw 5
saw 4
didn't see 4
didn't see 5
1
1
1

Well that's strange! The problem is the if statement. numbers_seen[nums[i]] == true doesn't evaluate the way one might expect. It doesn't convert numbers_seen[nums[i]] to bool and compare it to true, it converts true to int (1) and compares it to numbers_seen[nums[i]]. So:

If numbers_seem[nums[i]] is zero, line 15 executes it and sets it to 1.
If numbers_seem[nums[i]] is 1, line 12 executes and sets it to 2
If numbers_seem[nums[i]] is 2, then the condition 2==1 is false, so line 15 sets it back to 1.

Sometimes the obvious explanation of the symptom ("the values aren't getting incremented") turns out to be incorrect.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <vector>
#include <unordered_map>
#include <iostream>
#include <iomanip>

int main() {

	const std::vector<int> nums = { 1, 2, 3, 4, 5, 5, 5, 5, 5, 4, 3, 3, 1, 4, 1, 4, 5, 4, 4, 5 };

	std::unordered_map< int, int > numbers_seen;
	for( int v : nums ) ++numbers_seen[v] ; // update frequencies in map

	std::cout << "number  frequency\n"
	             "------  ---------\n" ;
	for( const auto& pair : numbers_seen ) // print out key, mapped value pairs
        std::cout << std::setw(4) << pair.first << std::setw(10) << pair.second << '\n' ;
}

http://coliru.stacked-crooked.com/a/9623281fdba284a2
http://rextester.com/JNAV48899
too few mentions of '43' in the output from other answers, or actually showcasing the disorder of the structure...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <iomanip>
#include <vector>
#include <unordered_map>

int main() 
{
    std::vector<int> nums { 1,2,3,4,5,5,5,5,5,43,3,1,4,1,4,5,4,4,5 };
    std::unordered_map<int, int> freq;

    for(auto n : nums)
        freq[n]++;

    for(auto& kv : freq)
        std::cout << std::setw(2) << kv.first << ": " << kv.second << '\n';

    return 0;
}

 5: 7
 4: 5
 3: 2
43: 1
 1: 3
 2: 1


As for explanation, the elements of maps and unordered maps are key-value std::pair s. Access the key with "first" and value with "second". These are weirdly named imo -- I would've just named them "key" and "value". Alas, c'est la vie.
Topic archived. No new replies allowed.