C++, Map<> structure but with Gaps - c++

I am trying to use a class to make a dictionary < key, value > in C++.
I found online that Map was the class I was suppoused to use.
However, when I try to use Map it FILLS the gaps between the keys.
This is an issue because keys are numbers, but they are incredibly sparse.
So in one set I may have the keys [1 , 20, 30000, 70000000]. I want my map to just store those 4 values, not every values between 1 and 70000000.
#include <iostream>
#include <map>
using namespace std;
int main()
{
map<int,int> a = {{1,-1},{20,200}}; // I want this to just store {1,20}
for(int x = 0; x < a.size(); x++) cout << a[p(x)] << ","; //however, when I print it I get [0,1..,19,20]
return 0;
}
OUTPUT
0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,
Is there some workaround to avoid C++ from "filling the gaps" or any other class in the STD that can be used for that purpose?

map::operator[] create entry for you (and increase it's size()). If you just want to iterate through std::map, use it's iterator.
for(auto& entry : a) cout << entry.second << ",";

As apple has commented, the operator[] creates the entry. See:
http://www.cplusplus.com/reference/map/map/operator[]/
mapped_type& operator[] (const key_type& k);
If k does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value.
If you want to check existence of the key, use map::find().

Here are some ways to print out the map: you can use a for-each loop or an iterator:
#include <iostream>
#include <map>
#include <random>
using namespace std;
int main()
{
map<int, int> myMap;
// filling map with random elements
random_device rd;
mt19937 rng(rd());
uniform_int_distribution<int> uni(0,1000);
for(int i = 0; i < 10; i++) {
// uses the [] operator to create an element
myMap[uni(rng)] = uni(rng);
}
// first method to print out map using for-each loop
for(auto a : myMap) {
// cannot change elements in map
cout << a.first << " " << a.second << endl;
}
// second method to print out map using iterator
for(map<int, int>::iterator it = myMap.begin(); it != myMap.end(); it++) {
// can change elements in map
cout << it->first << " " << it->second << endl;
}
}
Hope this helps!
By the way, thinking of using map is actually very intelligent when creating a dictionary, because map elements are automatically sorted! I assume you are mapping strings to ints, right?

Related

What is the best way traversing an unordered_map with a starting from a random element in C++?

I have an unordered_map of 'n' elements. It has a some eligible elements. I want to write a function such that each time, a random eligible element is picked.
Can this be achieved in the following time complexity?
Best case: O(1)
Avg case: O(1)
Worst case: O(n)
Referring - retrieve random key element for std::map in c++, I have come up with the following solution.
#include <iostream>
#include <unordered_map>
#include <random>
using namespace std;
void select_random_best(const std::unordered_map<std::string, int>& umap, const int random_start)
{
cout << "Selected random number " << random_start << endl;
auto it = umap.begin();
std::advance(it, random_start);
for(int i = 0; i < umap.size(); i++, it++) {
if(it == umap.end())
it = umap.begin();
// Check if the selected element satisfies the eligibility criteria.
// For the sake of simplicity, I am taking the following example.
if(it->second % 3 == 0) {
cout << it->first << ", " <<
it->second << endl;
return;
}
// Element not found continue searching
}
}
int main()
{
srand(time(0));
unordered_map<string, int> umap;
// inserting values by using [] operator
umap["a"] = 6;
umap["b"] = 3;
umap["f"] = 9;
umap["c"] = 2;
umap["d"] = 1;
umap["e"] = 3;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(0, umap.size() - 1);
const int random_start = distrib(gen);
select_random_best(umap, distrib(gen));
// another iteration
select_random_best(umap, distrib(gen));
cout << "Full list :" << endl;
// Traversing an unordered map
for (auto x : umap)
cout << x.first << ", " <<
x.second << "\t";
}
Can someone suggest if the use of std::advance() here would lead to the avg case time comlexity of O(1)? Or is there a better way of doing this?
std::unordered_map has forward iterators, which do not allow random access. Refer to iterator on the documentation page of the container.
Assuming all elements are eligible, std::advance() will go through size/2 elements on average. Because you only accept eligible elements, you will go through more than that. If you know the probability of the eligibility, you can estimate the average elements searched.
To achieve O(1) in the std::advance() step, you must use a data type with random access iterators, such as std::vector. However, the next step does not have constant compexity. In the worst case, you will go through all ineligible elements (not considering the possibility of an infinite loop if there are no eligible ones). So this approach is still O(n) as whole.
For the best performance, you need two lists: std::vector with only eligible elements, used for finding a random element, and std::unordered_map for other things.

How to get the elements of a tuple

I am creating a scrabble game and i need to have a basic score to words on the dictionary.
I used make_tuple and stored it inside my tuple. Is there a way to access elements in a tuple as if it was in a vector?
#include <iostream>
#include <tuple>
#include <string>
#include <fstream>
void parseTextFile()
{
std::ifstream words_file("scrabble_words.txt"); //File containing the words in the dictionary (english) with words that do not exist
std::ofstream new_words_file("test.txt"); //File where only existing words will be saved
std::string word_input;
std::tuple<std::string, int> tupleList;
unsigned int check_integrity;
int counter = 0;
while(words_file >> word_input)
{
check_integrity = 0;
for (unsigned int i = 0; i < word_input.length(); i++)
{
if((int)word_input[i] >= 97 && (int)word_input[i] <= 123) //if the letter of the word belongs to the alphabet
{
check_integrity++;
}
}
if(word_input.length() == check_integrity)
{
new_words_file << word_input << std::endl; //add the word to the new file
tupleList = std::make_tuple(word_input, getScore(word_input)); //make tuple with the basic score and the word
counter++; //to check if the amount of words in the new file are correct
std::cout << std::get<0>(tupleList) << ": " << std::get<1>(tupleList) << std::endl;
}
}
std::cout << counter << std::endl;
}
One would generally use a tuple when there are more than two values of different types to store. For just two values a pair is a better choice.
In your case what you want to achieve seems to be a list of word-value pairs. You can store them in a container like a vector but you can also store them as key-value pairs in a map. As you can see when following the link, an std::map is literally a collection of std::pair object and tuples are a generalization of pairs.
For completeness, if my understanding of your code purpose is correct, these are additions to your code for storing each tuple in a vector - declarations,
std::tuple<std::string, int> correct_word = {};
std::vector<std::tuple<std::string, int>> existing_words = {};
changes in the loop that saves existing words - here you want to add each word-value tuple to the vector,
if(word_input.length() == check_integrity)
{
// ...
correct_word = std::make_tuple(word_input, getScore(word_input));
existing_words.push_back(correct_word);
// ...
}
..and finally example of usage outside the construction loop:
for (size_t iv=0; iv<existing_words.size(); ++iv)
{
correct_word = existing_words[iv];
std::cout << std::get<0>(correct_word) << ": " << std::get<1>(correct_word) << std::endl;
}
std::cout << counter << std::endl;
The same code with a map would look like:
The only declaration would be a map from strings to values (instead of a tuple and vector of tuples),
std::map<std::string, int> existing_words = {};
In the construction loop you would be creating the map pair in a single line like this,
if(word_input.length() == check_integrity)
{
// ...
existing_words[word_input] = getScore(word_input);
// ...
}
While after constructing you would be accessing map elements using .first for the word and .second for the counter. Below is a printing example that also uses a for auto loop:
for (const auto& correct_word : existing_words)
std::cout << correct_word.first << ": " << correct_word.second << std::endl;
std::cout << counter << std::endl;
Notice that maps are by default alphabetically ordered, you can provide your own ordering rules and also use an unordered map if you don't want any ordering/sorting.

Initialize c++14 unordered_map with k buckets with key-value pair of zero

I am trying to create an unordered_map with minimum number of initial buckets(k), with all key-value pairs initialized to zero. Later, it should go inside the for loop and print values.
int someFunction(int k){
unordered_map<int, int> majority(k, {0,0});
}
for(auto m : majority){
cout << m.first << " " << m.second << " \n";
}
I get the following compilation(c++ 14) error:
no matching function for call to ‘std::unordered_map<int, int>::unordered_map(int&, <brace-enclosed initializer list>)
Is there a way I can achieve this?
unordered_map represents hash data structure. By hash definition every key must be unique. So you can`t initialize majority with several zero int keys.
Instead you can use reserve function :
void reserve ( size_type n );
Request a capacity change
Sets the number of buckets in the container (bucket_count) to the most
appropriate to contain at least n elements.
If n is greater than the current bucket_count multiplied by the
max_load_factor, the container's bucket_count is increased and a
rehash is forced.
If n is lower than that, the function may have no effect.
std::unordered_map only supports unique keys so that constructor doesn't exist.
If you want the same key multiple times you can use a std::unordered_multimap. See the reference for the available constructors.
#include <iostream>
#include <unordered_map>
int main() {
std::unordered_multimap<int, int> map;
const int k = 5;
for (int i = 0; i < k; ++i)
{
map.emplace(0, 0);
}
for (auto& kv: map)
{
std::cout << kv.first << " " << kv.second << std::endl;
}
}

how to print map in c++ without using iterator

Is it possible to print map in c++ without using iterator ?
something like
map <int, int>m;
m[0]=1;
m[1]=2;
for(int i =0; i<m.size(); i++)
std::cout << m[i];
Is it necessary to make iterator for printing map value ?
If you simply want to avoid typing out the iterator boilerplate, you can use a range-for loop to print each item:
#include <iostream>
#include <map>
int main() {
std::map<int,std::string> m = {{1, "one"}, {2, "two"}, {3, "three"}};
for (const auto& x : m) {
std::cout << x.first << ": " << x.second << "\n";
}
return 0;
}
Live example: http://coliru.stacked-crooked.com/a/b5f7eac88d67dafe
Ranged-for: http://en.cppreference.com/w/cpp/language/range-for
Obviously, this uses the map's iterators under the hood...
Is it necessary to make iterator for printing map value ?
Yes you need them, you can't know what keys been inserted in advance. And your code is not correct, think about
map <int, int>m;
m[2]=1;
m[3]=2;
for(int i =0; i<m.size(); i++)
std::cout << m[i]; // oops, there's not m[0] and m[1] at all.
// btw, std::map::operator[] will insert them in this case.

How to iterate through a specific key in a map containing vector as value?

How to iterate through the contents of map["a"] to retrieve call and call1 ?
std::vector<std::string> point
std::map<std::string, point> alloc
map["a"] = call, call1
map["i"] = call
I have tried using for loop using map iterator and inside that for loop another for loop on the vector and then checking whether the value of map iterator map equals "a" but keep getting an error.
I think you are misunderstanding some syntax and of the programming language and the semantics of the standard library containers a little bit. I will explain what I think you are doing wrong.
First thing is that you have a vector of string objects called point, this is an object not a type. An object is a variable of a type, for example
string name = "curious";
Here name is an object of type/class string, so you cannot type in point as the template parameter to the map, you have to type in a type. So that should be a string.
Second thing is that you are using the comma operator, I am not sure if you knew that you were doing that. The comma operator works as follows
#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
int main() {
cout << ("Hello", "World") << endl;
return 0;
}
^ this will generate a compiler error because the "Hello" is not used but the point is that the comma operator evaluates the first part of the expression and then returns the thing on the right; so this will print
World
Third thing is how you iterate through the map. When you iterate through a std::map in C++ you are actually iterating through a series of std::pairs so the following code
#include <iostream>
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <map>
using std::map;
int main() {
map<string, int> map_string_int {{"curious", 1}, {"op", 2}};
for (auto iter = map_string_int.begin(); iter != map_string_int.end();
++iter) {
cout << iter->first << " : " << iter->second << endl;
}
return 0;
}
will produce the following output
curious : 1
op : 2
the keys will be ordered alphabetically because they are stored in a binary search tree (https://en.wikipedia.org/wiki/Binary_search_tree)
Now I think you wanted to have a map from string objects to vectors, so you would structure your code as such
std::vector<string> point
std::map<string, std::vector<string>> alloc;
alloc["a"] = {"call", "call1"};
alloc["i"] = {"call"};
and you would iterate through this like so
for (auto iter = alloc.begin(); iter != alloc.end(); ++iter) {
cout << iter->first << " : " << iter->second << endl;
}
You would iterate through alloc["a"] like so
// sanity check
assert(alloc.find("a") != alloc.end());
for (auto iter = alloc["a"].begin(); iter != alloc["a"].end(); ++iter) {
cout << *iter << endl;
}
Hope that helped!
I assume you mean std::multimap instead of std::map, based on your use case (multiple values under the same key). It's in the same <map> header.
std::multimap<std::string, int> map;
map.insert(std::make_pair("first", 123));
map.insert(std::make_pair("first", 456));
auto result = map.equal_range("first");
for (auto it = result.first; it != result.second; ++it)
std::cout << " " << it->second;
Reference: std::multimap::equal_range
This should do what you want if I understand correctly.
std::vector<string> point = { "Hello", "World" };
std::map<std::string, decltype(point)> my_map;
//if you dont wan't to use decltype (or cant):
//std::map<std::string, std::vector<std::string>> my_map;
my_map["A"] = point;
my_map["B"] = { "Something", "Else" };
//this will iterate only trought my_map["A"]
for (const auto &vector_val : my_map["A"])
std::cout << vector_val << std::endl;
//this will iterate trought the whole map
for (const auto &map_pair : my_map)
{
std::cout << "map: " << map_pair.first << std::endl;
for (const auto &vector_val : map_pair.second)
std::cout << vector_val << std::endl;
std::cout << "---------------------------------" << std::endl;
}
I'm curious about knowing what is more suitable in such situations i.e multimap or map_of_vectors .
If sequencially someone want to iterate vector associated to a particular/all keys in map
what will be more efficient/optimal.
map<string ,vector<string>> mp;
// initialize your map...
for(auto itr=mp.begin(); itr!=mp.end() ;itr++)
for(auto itr2=itr->second.begin(); itr2!=itr->second.end() ;itr2++)
cout<<*itr2
for particular key just change first loop as stated down
auto itr=mp.find(key);