Code Errors - unordered_map program

In class we were assigned this problem:
https://open.kattis.com/problems/anewalphabet

And we're required to use unordered_map to do it. After a ton of research I eventually figured out how to use unordered_map (I think). But I keep getting errors. So... What am I doing wrong?

Errors:
(Please Note: The lines of the errors will be off because I removed a lot of notes to myself.)
1
2
3
4
5
6
7
8
9
ANewAlphabet.cpp: In function ‘std::__cxx11::string checkMap(std::__cxx11::string)’:
ANewAlphabet.cpp:124:43: error: ‘typedef class std::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > stringMap’ redeclared as different kind of symbol
     typedef unordered_map<string, string> stringMap;
                                           ^~~~~~~~~
ANewAlphabet.cpp:116:35: note: previous declaration ‘std::unordered_map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> > stringMap’
     unordered_map<string, string> stringMap;
                                   ^~~~~~~~~
ANewAlphabet.cpp:131:15: error: expected ‘;’ before ‘{’ token
     stringMap { {"a", "@"}, {"b", "8"}, {"c", "("}, {"d", "|)"}, {"e", "3"}


Full Code:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
Program: ANewAlphabet.cpp
Author: Piggy
Program Information:
  This program will replace normal letters of the alphabet with
  new characters (can range from 1-6 chars per letter).

Algorithm Steps:
1. Ask user to input a string.
2. Convert string to lowercase so it is NOT caSe-SeNSiTiVe.
3. call checkDict() function which will compare all of the characters
   in the string to those in the dictionary. Then will convert them to
   their translated form and return those.
  - Set up contents of unordered map in checkDict() function.
4. Output newly translated info.
5. Add assert() test cases (at least 3)
6. Review instructions to make sure I didn't forget anything.
  - Also comment out cout << "Please input a string: "; etc..
*/
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm> // for transform()
#include <assert.h>

// Will only be using this for map related stuff because
// I'm new to it and this makes it easier to learn.
using namespace std;

void test();
std::string transToLower(std::string uIn);
std::string checkMap(std::string userInput);


int main() {
    // Declaring variables
    std::string uInput, lowerUInput, result;
    
    // Automatically testing program:
    test();

    // Asks for user input and stores information into uInput
    std::cout << "Please input a string: ";
    std::getline(std::cin, uInput);

    // Makes user input all lowercase so checkDict() works properly.
    uInput = transToLower(uInput);
    //std::cout << "[Debug] uInput = " << uInput << "\n";
    
    // Checks unordered_map for each character in string
    // then returns translated values.
    result = checkMap(uInput);
    
    // Outputs result
    std::cout << result << "\n";

    return 0;
}


// Converts user input string to lowercase.
std::string transToLower(std::string uIn) {
    std::transform(uIn.begin(), uIn.end(), uIn.begin(), ::tolower);
    return uIn;
}


// Checks dictionary for each character in string then returns translated values.
std::string checkMap(std::string userInput) {
    // Declares an unordered map of type <string, string> called stringMap.
    // AKA <key in map, mapped value>. Example: {"a", "@"}
    unordered_map<string, string> stringMap;
	
    const typedef unordered_map<string, string> stringMap;

    // unordered_map
    stringMap { {"a", "@"}, {"b", "8"}, {"c", "("}, {"d", "|)"}, {"e", "3"}
                {"f", "#"}, {"g", "6"}, {"h", "[-]"}, {"i", "|"},
                {"j", "_|"}, {"k", "|<"}, {"l", "1"}, {"m", "[]\\/[]"},
                {"n", "[]\\[]"}, {"o", "0"}, {"p", "|D"}, {"q", "(,)"},
                {"r", "|Z"}, {"s", "$"}, {"t", "']['"}, {"u", "|_|"},
                {"v", "\\/"}, {"w", "\\/\\/"}, {"x", "}{"}, {"y", "`/"},
                {"z", "2"} };

    // Need to update the return once done
    return "0";
}


void test() {
    assert(transToLower("THIS is a TEST") == "this is a test");
    //assert();
    //assert();
    //assert();

    std::cout << "All test cases passed! \n";
}

Can also be seen here if desired: https://pastebin.com/NMQ8hTTW

Help would be greatly appreciated! Thanks!
Last edited on
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm> // for transform()
#include <cassert> // <assert.h>
#include <cctype> // for std::tolower

std::string transToLower(std::string uIn);
std::string translate(std::string userInput);

int main() {

    const std::string str = "This ~ IS ~ A ~ test ~ !" ;
    std::cout << str << '\n' ;

    const std::string lc_str = transToLower(str) ;
    std::cout << lc_str << '\n' ;

    const std::string translated_str = translate(str) ;
    std::cout << translated_str << '\n' ;
}

// Converts user input string to lowercase.
std::string transToLower(std::string uIn) {

    std::transform( uIn.begin(), uIn.end(), uIn.begin(),
                    []( char c ) { return std::tolower(c) ; } );
    return uIn;
}


// Checks dictionary for each character in string then returns translated string.
std::string translate( std::string input ) {

    // Declares an unordered map of type <char, string> called stringMap.
    // AKA <key in map, mapped value>. Example: {'a', "@"}
    static const std::unordered_map<char, std::string> stringMap
        { {'a', "@"}, {'b', "8"}, {'c', "("}, {'d', "|)"}, {'e', "3"},
        {'f', "#"}, {'g', "6"}, {'h', "[-]"}, {'i', "|"},
        {'j', "_|"}, {'k', "|<"}, {'l', "1"}, {'m', "[]\\/[]"},
        {'n', "[]\\[]"}, {'o', "0"}, {'p', "|D"}, {'q', "(,)"},
        {'r', "|Z"}, {'s', "$"}, {'t', "']['"}, {'u', "|_|"},
        {'v', "\\/"}, {'w', "\\/\\/"}, {'x', "}{"}, {'y', "`/"},
        {'z', "2"} };

    std::string result ;
    for( char c : transToLower(input) ) // for each character in input converted to lower case
    {
        const auto iter = stringMap.find(c) ; // try to find it in the map

        if( iter != stringMap.end() ) // if found
            result += iter->second ; // append the translated value to the result

        else result += c ; // otherwise, append the character as it is
    }

    return result ;
}

http://coliru.stacked-crooked.com/a/e55bdbdeeaf17ff4
@JLBorges

Thanks! I have a few questions for your code.

1.) Why did you put static for the unordered map? Is it so the map doesn't need to be redeclared every time the function is called?
2.) What is the : doing in for(char c : transToLower(input) )?
3.) What does "auto" do for const auto iter stringMap.find(c); and why is it const?
4.) What does -> do in general? result += iter->second ;
5.) This is a lambda expression right? I've only been briefly introduced to them.
[]( char c ) { return std::tolower(c) ; } );
Why is this useful? Couldn't you just declare c somewhere then replace that code with tolower(c). Can lambdas be used elsewhere? If so, how'd it look to re-use it?

Thanks!
> Why did you put static for the unordered map?
> Is it so the map doesn't need to be redeclared every time the function is called?

Yes. We want to initialise the map just once and then reuse it every time the function is called after that.


> What is the : doing in for(char c : transToLower(input) )?

It is a range based loop: for each character 'c' in the input translated to lower case.
see: http://www.stroustrup.com/C++11FAQ.html#for


> What does "auto" do for const auto iter stringMap.find(c);

With auto, we want the compiler to figure out what is the correct type for 'iter'
see: http://www.stroustrup.com/C++11FAQ.html#auto


> and why is it const?

There is no intent that it should ever be modified at a later stage.
http://www.cplusplus.com/forum/beginner/180986/#msg888218


> What does -> do in general? result += iter->second ;

In general, -> is the pointer to member operator:
p->m gives access to the member m of an object pointed to by p

The semantics of an iterator are akin to that of a pointer;
iter->second accesses the member second of the pair 'pointed to' by iter


> Can lambdas be used elsewhere? If so, how'd it look to re-use it?

We can keep the result of evaluating the lambda expression (a closure object) and reuse it later.

For example: const auto my_is_lower = []( char c ) { return std::tolower(c) ; };
We can then reuse my_is_lower.
@JLBorges
Ahh okay very informative! Thank you!

Only thing I'm struggling to understand is the -> but I will probably need to figure out what exactly pointers do before I can understand that concept.


Okay, new question. I'm trying to make my own version that uses some of the things from my class notes.
(Specifically: https://gyazo.com/68c1785ddb10f521956ffbf60009f894)

Errors:
1
2
3
4
5
6
7
8
9
ANewAlphabet.cpp: In function ‘std::__cxx11::string checkMap(std::__cxx11::string)’:
ANewAlphabet.cpp:198:41: error: passing ‘const std::unordered_map<char, std::__cxx11::basic_string<char> >’ as ‘this’ argument discards qualifiers [-fpermissive]
             result = result + valueMap[c]; // Doesn't seem to work?
                                         ^
In file included from /usr/include/c++/7/unordered_map:48:0,
                 from ANewAlphabet.cpp:61:
/usr/include/c++/7/bits/unordered_map.h:975:7: note:   in call to ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = char; _Tp = std::__cxx11::basic_string<char>; _Hash = std::hash<char>; _Pred = std::equal_to<char>; _Alloc = std::allocator<std::pair<const char, std::__cxx11::basic_string<char> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::__cxx11::basic_string<char>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = char]’
       operator[](const key_type& __k)
       ^~~~~~~~

My code:
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
    // Please note: This is just a snippet of the relevant parts.
    static const unordered_map<char, string> valueMap {
        {'a', "@"}, {'b', "8"}, {'c', "("}, {'d', "|)"},
        {'e', "3"}, {'f', "#"}, {'g', "6"}, {'h', "[-]"},
        {'i', "|"}, {'j', "_|"}, {'k', "|<"}, {'l', "1"},
        {'m', "[]\\/[]"}, {'n', "[]\\[]"}, {'o', "0"}, {'p', "|D"},
        {'q', "(,)"}, {'r', "|Z"}, {'s', "$"}, {'t', "']['"},
        {'u', "|_|"}, {'v', "\\/"}, {'w', "\\/\\/"}, {'x', "}{"},
        {'y', "`/"}, {'z', "2"}
    };

    // for each char in userInput string:
    for (int i = 0; i < userInput.size(); i++) {
        char c = userInput[i];
        // if c is found in valueMap (unordered_map) then do:
        if (valueMap.count(c) == 1)
            // add valueMap[c] (value) to result string.
            result = result + valueMap[c]; // Doesn't seem to work?
            //result.append(valueMap[c]); // Doesn't seem to work?
        else {
            std::string strC = to_string(c); // adds char c to a str so I can add to result
            result = result + strC;
        }
    }
    cout << result << "\n";


Help would be appreciated! Thanks!
Last edited on
To use operator[], the unordered_map must be non-const
(it inserts the key into the map if the key does not exist").

Instead, we can use at() on the const unordered_map.
https://en.cppreference.com/w/cpp/container/unordered_map/at

1
2
3
4
5
6
7
8
9
10
// for each char in userInput string:
for (int i = 0; i < userInput.size(); i++) {

    const char c = userInput[i];

    if (valueMap.count(c) == 1) // if c is found in valueMap (unordered_map) then do:
        result = valueMap.at(c) ; // add valueMap.at(c) (value) to result string.

    else result = result + c ; // else append char c to the result string
}
Last edited on
Oh okay. Thanks!

Although it doesn't output the proper answer.

Also, why is const char c = userInput[i]; a constant? Isn't it not constant because the makes it change with the for loop?

Please Note: I tried both with the const and without it. Both had same answer.

Both times I input: HELLO world!
And it output |)!.
It [i]should
output: [-]3110 \/\/0|Z1|)

Thanks! :)

Code of the Function:
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
27
28
std::string checkMap(std::string userInput) {
	std::string result;

	static const unordered_map<char, string> valueMap {
		{'a', "@"}, {'b', "8"}, {'c', "("}, {'d', "|)"},
		{'e', "3"}, {'f', "#"}, {'g', "6"}, {'h', "[-]"},
		{'i', "|"}, {'j', "_|"}, {'k', "|<"}, {'l', "1"},
		{'m', "[]\\/[]"}, {'n', "[]\\[]"}, {'o', "0"}, {'p', "|D"},
		{'q', "(,)"}, {'r', "|Z"}, {'s', "$"}, {'t', "']['"},
		{'u', "|_|"}, {'v', "\\/"}, {'w', "\\/\\/"}, {'x', "}{"},
		{'y', "`/"}, {'z', "2"}
	};

	// for each char in userInput:
	for (int i = 0; i < userInput.size(); i++) {
		const char c = userInput[i];

		// if c is found in valueMap (unordered_map) then do:
		if (valueMap.count(c) == 1)
			result = valueMap.at(c); // add valueMap.at(c) (the value in the map) to result string.
		else
			result = result + c; // appends const char c to the result string
		
	}

	// Need to update the return once done
	return result;
}

Note: main() outputs the result.


Last edited on
> it doesn't output the proper answer.

There was a careless error (my mistake) in the earlier code;
on lines 20 and 22, it should be += (append) instead of = (assign).

In addition, since the keys in the map are lower case characters,
you may want to convert the user input to lower case first.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
#include <string>
#include <unordered_map>
#include <algorithm> // for transform()
#include <cassert> // <assert.h>
#include <cctype> // for std::tolower

std::string transToLower( std::string uIn );
std::string checkMap( std::string userInput );

int main() {

    const std::string str = "HELLO world!" ;
    std::cout << str << '\n' ;

    const std::string translated_str = checkMap(str) ;
    std::cout << translated_str << '\n' ;
}

// Converts user input string to lowercase.
std::string transToLower(std::string uIn) {

    std::transform( uIn.begin(), uIn.end(), uIn.begin(),
                    []( char c ) { return std::tolower(c) ; } );
    return uIn;
}

std::string checkMap(std::string userInput) {

	std::string result;

	static const std::unordered_map<char, std::string> valueMap {
		{'a', "@"}, {'b', "8"}, {'c', "("}, {'d', "|)"},
		{'e', "3"}, {'f', "#"}, {'g', "6"}, {'h', "[-]"},
		{'i', "|"}, {'j', "_|"}, {'k', "|<"}, {'l', "1"},
		{'m', "[]\\/[]"}, {'n', "[]\\[]"}, {'o', "0"}, {'p', "|D"},
		{'q', "(,)"}, {'r', "|Z"}, {'s', "$"}, {'t', "']['"},
		{'u', "|_|"}, {'v', "\\/"}, {'w', "\\/\\/"}, {'x', "}{"},
		{'y', "`/"}, {'z', "2"}
	};

        userInput = transToLower(userInput) ; // *** added

	// for each char in userInput:
	for ( std::size_t i = 0; i < userInput.size(); ++i ) {

		const char c = userInput[i];

		// if c is found in valueMap (unordered_map) then do:
		if (valueMap.count(c) == 1)
			result += valueMap.at(c); // add valueMap.at(c) (the value in the map) to result string.
			// *** corrected: +=
		else
			result += result + c; // appends const char c to the result string
			// *** corrected: +=

	}

	// Need to update the return once done
	return result;
}

echo && echo && g++ -std=c++2a -O3 -Wall -Wextra -pedantic-errors -pthread -march=native main.cpp && ./a.out 
echo && echo && clang++ -std=c++2a -O3 -Wall -Wextra -pedantic-errors -pthread -march=native main.cpp -lsupc++ && ./a.out


HELLO world!
[-]3110[-]3110 \/\/0|Z1|)[-]3110[-]3110 \/\/0|Z1|)!


HELLO world!
[-]3110[-]3110 \/\/0|Z1|)[-]3110[-]3110 \/\/0|Z1|)!

http://coliru.stacked-crooked.com/a/181fe6e09f57c102
https://rextester.com/IRIG81991
Ahh okay, whoops I should've noticed that too.

Although the output is still incorrect. It should be:
[-]3110 \/\/0|Z1|)!

And currently it is:
[-]3110[-]3110 \/\/0|Z1|)[-]3110[-]3110 \/\/0|Z1|)!

Meaning for some reason it is printing the equivilent of:
hellohello worldhellohello world!

Instead of:
hello world!

Edit: Oh I think it's because it does the for loop every time it is checking a repeated character in hello such as L.

Edit 2: Thoroughly confused on how to fix that though.

Edit 3: Nevermind, my solution in "Edit: ..." doesn't make sense for the output.

Edit 4: Just caught the mistake. result += result + c; should be result += c. That fixes the issue!

Everything is working as intended. Thank you a ton for your help!
Last edited on
Topic archived. No new replies allowed.