//http://www.cplusplus.com/doc/tutorial/files/ // reading a text file #include <iostream> #include <fstream> #include <string> #include <map> #include <iomanip> #include <cstdlib> using namespace std; map<string,int> words;// Words found void put(string word) { ++words[word]; } void print() { for (auto iter = words.begin(); iter != words.end(); iter++) { cout << "[" << iter->first << "] = " << iter->second << endl; } } void print(map<string, int>& M) { // Declare a multimap multimap<int, string, std::greater<int>> MM; // Insert every (key-value) pairs from // map M to multimap MM as (value-key) // pairs for (auto& it : M) { MM.insert({ it.second, it.first }); } // Print the multimap for (auto& it : MM) { cout<< std::setw(15) << it.second << ' ' << it.first << endl; } cout<< "size: "<< M.size(); } void tokenizer(string text) { const std::string separators { " ,;:.\"!?'\n(){}*" }; // Word delimiters size_t start { text.find_first_not_of(separators) }; // First word start index while (start != std::string::npos) { // Find the words size_t end = text.find_first_of(separators, start + 1); // Find end of word if (end == std::string::npos) // Found a separator? end = text.length(); // No, so set to end of text //words.push_back(text.substr(start, end - start)); // Store the word put(text.substr(start, end - start)); start = text.find_first_not_of(separators, end + 1); // Find first character of next word } } string h1=R"(D:\HarryPotter\harry potter7.txt)"; int main () { string line; string text {}; ifstream myfile (h1); if (myfile.is_open()) { while ( getline (myfile,line) ) { //cout << line << '\n'; text+=line; } myfile.close(); } else cout << "Unable to open file"; tokenizer(text); print(words); return 0; }
更明智的用现代c++ map
std::map and its siblings (std::multimap, std::unordered_map/multimap) used to be my favourite containers when I was doing competitive programming. In fact, I still like them(though using less frequently nowadays). And with Modern C++, we now have more reasons to use std::map. That's why I have decided to address this topic by writing an article summarizing these new features. So, without much gibberish, let's dive-in directly.
std::map::contains (C++20)
std::map::contains member function is a good step towards code expressiveness. And I am also tire of writing:Java
1
if (auto search = freq_of.find(2); search != freq_of.end()) {
2
cout << "Found" << endl;
3
}
4
// Where assume, freq_of = map<uint32_t, uint32_t>{{3, 1}, {1, 1}, {2, 1}};
Java
1
if (freq_of.contains(2)) {
2
cout << "Found" << endl;
3
}
The code we write is written first for human consumption & only secondarily for the computer to understand.
- John Sonmez
std::map::try_emplace (C++17)
While inserting into the map, we have 2 different possibilities:- The key doesn't exist yet. Create a fresh key-value pair.
- The key does exist already. Take the existing item and modify it.
std::map is by using operator[ ], std::map::insert or std::map::emplace . But, in all of these cases, we have to bear the cost of default/specialized constructor or assignment call. And the worst part is if an item already exists, we have to drop the freshly created item.Java
1
int main() {
2
vector v{3, 4, 5, 8, 7, 3, 5, 2, 4};
3
map<uint32_t, uint32_t> freq_of;
4
5
for (const auto &n : v) {
6
if (const auto &[it, inserted] = freq_of.emplace(n, 1); !inserted) {
7
it->second++; // Exists already
8
}
9
}
10
11
assert(freq_of[3] == 2);
12
13
return EXIT_SUCCESS;
14
}
Java
1
if (const auto &[it, inserted] = freq_of.try_emplace(n, 1); !inserted) {
2
it->second++;
3
}
Although the above example hasn't showcased the expensive to create items. But, yes! whenever you encounter such a situation, must be known how to handle it with
std::map::try_emplace.std::map::insert_or_assign (C++17)
When you have to insert element anyhow. For the sake of convenience, you use std::map::operator[ ]. Which is OK( and dangerous)! Unless you have any constraint on insertion or assignment.For example, while counting the frequency of elements with the added constraint that when an element is repeated(i.e. assigned) you have to remove all the element lesser than the current one.
Java
1
int main() {
2
vector v{8, 3, 9, 5, 8};
3
map<uint32_t, uint32_t> freq_of;
4
5
for (auto &&n : v) {
6
const auto &[it, is_inserted] = freq_of.insert_or_assign(n, 1);
7
8
if (!is_inserted) { // remove all lesser element then current one if repeated
9
freq_of.erase(begin(freq_of), it);
10
}
11
}
12
13
assert((freq_of == decltype(freq_of){
14
{8, 1},
15
{9, 1},
16
}));
17
18
return EXIT_SUCCESS;
19
}
std::map::insert With Hint (C++11/17)
Looking up items in anstd::map takes O(log(n)) time. This is the same for inserting new items. Because the position where to insert them must looked up. Naive insertion of M new items would thus take O(M * log(n)) time.In order to make this more efficient,
std::map insertion functions accept an optional insertion hint parameter. The insertion hint is basically an iterator, which points near the future position of the item that is to be inserted. If the hint is correct, then we get amortized O(1) insertion time.Java
1
int main() {
2
map<uint32_t, string> m{{2, ""}, {3, ""}};
3
auto where(end(m));
4
5
for (const auto &n : {8, 7, 6, 5, 4, 3, 2, 1}) { // Items in non-incremental order
6
where = m.insert(where, {n, ""});
7
}
8
9
// How it is not done!
10
// m.insert(end(m), {0, ""});
11
12
for (const auto &[key, value] : m) {
13
cout << key << " : " << value << endl;
14
}
15
16
return EXIT_SUCCESS;
17
}
O(log(n)) performance again.For the above example, the first insertion, we got the end iterator of the map, because we had no better hint to start with. After installing an 8 in the tree, we knew that installing 7 will insert a new item just in front of the 8, which qualified it to be a correct hint. This applies to 6 as well, if put into the tree after inserting the 7, and so on. This is why it is possible to use the iterator, which was returned in the last insertion for the next insertion.
quick-benchmark.
The thing here to note is what happens when there are duplicates! The duplicated elements are not transferred. They're left behind in the right-hand-side map.
**Note:* It is important to know that before C++11, insertion hints were considered correct when they pointed before the position of the newly inserted item.*
std::map::merge (C++17)
Same as std::list:splice, which transfers the elements from one list to another. we havestd::map::merge which can merge the two same type of std::map.Java
1
int main() {
2
map<uint32_t, string> fruits{{5, "grapes"}, {2, "tomoto"}};
3
map<uint32_t, string> person{{2, "mickel"}, {10, "shree"}};
4
map<uint32_t, string> fruits_and_persons;
5
6
fruits_and_persons.merge(fruits);
7
assert(fruits.size() == 0);
8
9
fruits_and_persons.merge(person);
10
assert(person.size() == 1);
11
assert(person.at(2) == "mickel"); // Won't overwrite value at 2 i.e.`mickel`
12
13
assert((fruits_and_persons == decltype(fruits){
14
{2, "tomoto"},
15
{5, "grapes"},
16
{10, "shree"},
17
}));
18
19
return EXIT_SUCCESS;
20
}
std::map::extract (C++17)
Unlikestd::map::merge that transfers the elements in bulk, std::map::extract along with std::map::insert transfers element piecewise. But what is the more compelling application of std::map::extract is modifying keys.const qualifier is added to the key type.
This kind of restriction is perfectly valid because it makes harder for the user to use
std::map the wrong way. But what if we really need to change the keys of some map items?Java
1
int main() {
2
map<int, string> race_scoreboard{{1, "Mickel"}, {2, "Shree"}, {3, "Jenti"}};
3
using Pair = map<int, string>::value_type;
4
5
{
6
auto Jenti(race_scoreboard.extract(3));
7
auto Mickel(race_scoreboard.extract(1));
8
9
swap(Jenti.key(), Mickel.key());
10
11
auto [it, is_inserted, nh] = race_scoreboard.insert(move(Jenti)); // nh = node handle
12
assert(*it == Pair(1, "Jenti") && is_inserted == true && nh.empty());
13
14
race_scoreboard.insert(move(Mickel));
15
}
16
17
assert((race_scoreboard == decltype(race_scoreboard){
18
{1, "Jenti"},
19
{2, "Shree"},
20
{3, "Mickel"},
21
}));
22
23
return EXIT_SUCCESS;
24
}
std::map to imitate the racing position. And after a while, Jenti took the lead and Mickel left behind. In this case, how we have switched the keys(position on a race track) of those players.