Access the values (array) of unordered_map c++ - c++

I am trying to use the unordered_map in C++, such that, for the key I have a string, while for the value there is an array of floats.
std::unordered_map<std::string, std::array<float, 3>> umap;
But, I am not sure how to access the array of values. I know to access the elements, an iterator is an option, but how specifically elements of an array can be accessed?
I am trying to assign these array values to different array (std::array mapArrayVal)
I tried using
for (auto i = umap.begin(); i != umap.end(); i++)
{
std::array<float, 3> mapArrayVal = (i->second.first, i->second.second,
i>second.third);
}
is the correct way? Any help is appreciated, TIA!

This example shows you how to do it with some comments to help you on your way :
#include <array>
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
// a map consists of key,value pairs in your case
// the key will have a type of std::string
// the value will be an std::array (with three entries)
std::unordered_map<std::string, std::array<float, 3>> umap{
{"key1", {1.0,2.0,3.0}},
{"key2", {4.0,5.0,6.0}}
};
// iterate over all entries using an explicitly type it
// normally you would type auto i.o. std::unordered_map<std::string, std::array<float, 3>>::iterator
// but this shows all the types involved
for (std::unordered_map<std::string, std::array<float, 3>>::iterator it = umap.begin(); it != umap.end(); ++it)
{
// access key/values by iterator it->second will be the array
std::cout << "key = `" << it->first << "`, values : {" << (it->second)[0] << ", " << (it->second)[1] << ", " << (it->second)[2] << "}\n";
}
// however with c++ you could do it in a much more readable way
// combine range based for loop : https://en.cppreference.com/w/cpp/language/range-for
// with structured binding : https://en.cppreference.com/w/cpp/language/structured_binding
// the key_value_pair is const since you only want to observe it for printing
for (const auto& [key, values] : umap)
{
std::cout << "key = `" << key << "`, values : {" << values[0] << ", " << values[1] << ", " << values[2] << "}\n";
}
// use at(key) in map don't use operator[] it may insert an "empty" item in the map if something isn't found there!
auto& reference_to_array_in_map = umap.at("key1");
// this is how you make a copy of the array
std::array<float, 3> copied_values{ reference_to_array_in_map };
for (const float value : copied_values)
{
std::cout << value << " ";
}
std::cout << "\n";
return 0;
}

Related

How are pairs stored in memory in C++?

I was just reading about pairs in C++ when this doubt stroke my mind that how the pairs are stored in memory and id the identifier assigned to the pairs a object or something else.
pls explain how an array containing pair uses memory to save the pairs and how can we iterate through the that array, by accessing each pair;
As for the pair itself, if you take a look at the standard library source code you'll just notice, that after cutting all the boilerplate, the for the most trivial case std::pair is just a simple class template:
template<typename First, typename Second>
struct pair
{
First first;
Second second;
};
Now, all the boilerplate is there to ensure the all the special functions like comparison, assignment, copy construction etc. are performed with minimal overhead.
But for the sake of mental model one can think of this simple struct.
As for "array of pairs" - I'm not sure I follow, really.
std::array<std::pair<X,Y>, SIZE>/std::vector<std::pair<X,Y>> behaves just as it would for any other type, i.e. it store the pairs in contiguous memory block, end of story.
Same about iteration, there's nothing special about it:
std::array<std::pair<char, int>, 3> pairs{
std::pair{'a', 1},
std::pair{'b', 2},
std::pair{'c', 3}};
for (const auto& p:pairs){
std::cout << p.first << " " << p.second << "\n";
}
demo
Take a look at this example:
#include <iostream>
#include <array>
#include <iterator>
int main( )
{
std::array< std::pair<char, char>, 10 > arrayOfPairs { };
std::cout << "size of array: " << sizeof( arrayOfPairs ) << "\n\n";
for ( size_t idx { }; idx < arrayOfPairs.size( ); ++idx) // fill the array with
// std::pair objects
{
arrayOfPairs[ idx ] = std::make_pair<char, char>( 'a', 'b' + idx );
}
std::cout << "key" << " " << "value" << '\n';
for ( const auto& p : arrayOfPairs ) // print the keys and values
{
std::cout << " " << p.first << " " << p.second << '\n';
}
return 0;
}
Output:
size of array: 20
key value
a b
a c
a d
a e
a f
a g
a h
a i
a j
a k
In this example, each pair object consists of two chars, so the size is 2 bytes. arrayOfPairs has the space for 10 pair objects which means that its size is 10 * 2 == 20 bytes. In an std::pair object, the key and its value are stored besides each other. It acts like a simple struct.

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.

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);

Moving objects from one unordered_map to another container

My question is that of safety. I've searched cplusplus.com and cppreference.com and they seem to be lacking on iterator safety during std::move. Specifically: is it safe to call std::unordered_map::erase(iterator) with an iterator whose object has been moved? Sample code:
#include <unordered_map>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
class A {
public:
A() : name("default ctored"), value(-1) {}
A(const std::string& name, int value) : name(name), value(value) { }
std::string name;
int value;
};
typedef std::shared_ptr<const A> ConstAPtr;
int main(int argc, char **argv) {
// containers keyed by shared_ptr are keyed by the raw pointer address
std::unordered_map<ConstAPtr, int> valued_objects;
for ( int i = 0; i < 10; ++i ) {
// creates 5 objects named "name 0", and 5 named "name 1"
std::string name("name ");
name += std::to_string(i % 2);
valued_objects[std::make_shared<A>(std::move(name), i)] = i * 5;
}
// Later somewhere else we need to transform the map to be keyed differently
// while retaining the values for each object
typedef std::pair<ConstAPtr, int> ObjValue;
std::unordered_map<std::string, std::vector<ObjValue> > named_objects;
std::cout << "moving..." << std::endl;
// No increment since we're using .erase() and don't want to skip objects.
for ( auto it = valued_objects.begin(); it != valued_objects.end(); ) {
std::cout << it->first->name << "\t" << it->first.value << "\t" << it->second << std::endl;
// Get named_vec.
std::vector<ObjValue>& v = named_objects[it->first->name];
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
// And then... is this also safe???
it = valued_objects.erase(it);
}
std::cout << "checking... " << named_objects.size() << std::endl;
for ( auto it = named_objects.begin(); it != named_objects.end(); ++it ) {
std::cout << it->first << " (" << it->second.size() << ")" << std::endl;
for ( auto pair : it->second ) {
std::cout << "\t" << pair.first->name << "\t" << pair.first->value << "\t" << pair.second << std::endl;
}
}
std::cout << "double check... " << valued_objects.size() << std::endl;
for ( auto it : valued_objects ) {
std::cout << it.first->name << " (" << it.second << ")" << std::endl;
}
return 0;
}
The reason I ask is that it strikes me that moving the pair from the unordered_map's iterator may (?) therefore *re*move the iterator's stored key value and therefore invalidate its hash; therefore any operations on it afterward could result in undefined behavior. Unless that's not so?
I do think it's worth noting that the above appears to successfully work as intended in GCC 4.8.2 so I'm looking to see if I missed documentation supporting or explicitly not supporting the behavior.
// move object :: IS THIS SAFE??
v.push_back(std::move(*it));
Yes, it is safe, because this doesn't actually modify the key. It cannot, because the key is const. The type of *it is std::pair<const ConstAPtr, int>. When it is moved, the first member (the const ConstAPtr) is not actually moved. It is converted to an r-value by std::move, and becomes const ConstAPtr&&. But that doesn't match the move constructor, which expects a non-const ConstAPtr&&. So the copy constructor is called instead.

C++ STL map with custom comparator storing null pointers

I'm trying to write a copy constructor for an object managing a STL map containing pointers, where the key is a string. However, when I attempt to insert new values in the map, the pointers are set to NULL:
// ...
for(std::map<std::string, data_base*, order>::const_iterator it = other.elements.begin();
it != other.elements.end(); ++it){
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
elements[it->first] = t;
std::cout << "INSERTED: " << std::hex << elements[it->first] << std::endl;
}
// ...
other is the object being copied and elements the map. The clone() method returns a pointer to a new object (via new).
Running the code above I get something like:
CLONE: 0xcfbbc0
INSERTED: 0
I'm not a very experienced programmer and this issue is probably simple to fix, but I didnt find any solution to it searching around.
Thanks a lot for your time.
I don't see any problem with this code, other than maybe
std::map<std::string, data_base*, order>::const_iterator it
Here order gives the key comparator to use to sort the pairs contained in the map (often implemented as a tree).
Maybe you're doing something wrong in it, making your [] operator don't find the right ke, making your last line logging a new pair with a null ptr.
First, try without that order, using the default key-comparator (std::less), then if it don't work, post your order definition and the map declaration. If it's not enough, just provide a simple complete program that reproduce the problem.
I just wrote a simple similar test, using the default key-comparator :
#include <map>
#include <string>
#include <iostream>
struct Data
{
int k;
Data* clone() { return new Data(); }
};
typedef std::map< std::string, Data* > DataMap;
DataMap data_map;
int main()
{
data_map[ "hello" ] = new Data();
data_map[ "world" ] = new Data();
DataMap other_map;
for( DataMap::const_iterator it = data_map.begin(); it != data_map.end(); ++it)
{
Data*t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
other_map[it->first] = t;
std::cout << "INSERTED: " << std::hex << other_map[it->first] << std::endl;
}
std::cin.ignore();
return 0;
}
On VS2010SP1, this outputs :
CLONE: 00034DD0
INSERTED: 00034DD0
CLONE: 00035098
INSERTED: 00035098
So it should be the problem, or maybe you're doing something wrong before.
Try this out, to help debug the issue. I'd recommend double-checking that the order function is correct. You can remove it to use std::less<T>, which is known to work.
// ...
typedef std::map<std::string, data_base*, order> string_db_map;
for(string_db_map::const_iterator it = other.elements.begin();
it != other.elements.end();
++it)
{
data_base *t = it->second->clone();
std::cout << "CLONE: " << std::hex << t << std::endl;
std::pair<string_db_map::iterator, bool) result = elements.insert(
string_db_map::value_type( it->first, t));
if ( !result.second )
{
std::cout << "element['" << it->first << "'] was already present, and replaced." << std::endl;
}
std::coud << "INSERTED [iterator]: " << std::hex << (*result.first).second << std::endl;
std::cout << "INSERTED [indexed]: " << std::hex << elements[it->first] << std::endl;
}
// ...