I have a std::map<std::string, std::vector<int>>. Is there a way to provide a "view" of that map to a function that takes a variable of the same type? Specifically, is there a way to slice the vectors within the map, yet provide a view (sliced) that is compliant to the std::map interface? Something similar to boost range adapters or indexes, but for nested structures.
I am mainly looking for something via boost, but I am open to other suggestions as well.
[UPDATE] the goal is to "NOT" copy or move the map, only access its vectors according to the slicing criteria. And the function that takes the map as a variable should not be aware of the slicing. I hope this makes the question clearer.
Here's a pseudo example:
map<string, vector<int>> my_map;
my_map["a"] = {0,1,2,3,4,5};
my_map["b"] = {0,1,2,3,4,5};
my_map["c"] = {0,1,2,3,4,5};
map<string, pair<int>> slices;
slices["a"] = {1,4};
slices["b"] = {2,3};
slices["c"] = {0,5};
map_view = magic(my_map, slices);
cout << "a: " << print_vector(map_view["a"]) << endl;
cout << "b: " << print_vector(map_view["b"]) << endl;
cout << "c: " << print_vector(map_view["c"]) << endl;
//desired output
a: 1,2,3
b: 2
c: 0,1,2,3,4
No magic needed try this:
auto& ref_map = my_map["a"];
auto& ref_slice = slices["a"];
std::cout << "a: ";
std::copy (
ref_map.begin() + ref_slice.first,
ref_map.begin() + ref_slice.second,
std::ostream_iterator<int> (std::cout,", ")
);
I ended up adding a function that takes one of the map values and a slicing criteria, then returns a joined boost range over multiple boost slices, depending on the slicing criteria. In addition to that, I resorted to using auto return type inference of C++14 to avoid messing with the actual return types of boost adaptors and ranges.
Here's a quick snippet:
const auto get_map_view(
string key,
const map<string, vector<int>> & my_map,
const pair<int,int> & slice, bool exclude=false) {
const auto & values = my_map.at(key);
if (!exclude) {
return boost::range::join(
values | boost::adaptors::sliced(0, 0),
values | boost::adaptors::sliced(slice.first, slice.second));
} else {
return boost::range::join(
values | boost::adaptors::sliced(0, slice.first),
values | boost::adaptors::sliced(slice.second, values.size());
}
}
( auto a)[&]{ auto bounds = slices[a];
auto v = mymap[a];
return find(v.begin(),v.end(), bounds.first);}
This for example will get you an iterator to the front edge based on your criterion the rest should be trivial with lambdas.
Related
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.
I would like to loop through two maps at the same time, how could I achieve this?
I have two vectors want to print both, can I do two time (auto it : mymap) within one for? Something like:
for (auto it: mymap && auto on: secondMap)
is this even allowed?
I am trying to print values like (value1, value2) where each of the values is in a different map. The maps do not necessarily contain the exact same items but the key is an Instruction and the value is an integer, so if I have a element in the map for value2, then not necessarily there is a value1 corresponding to the same key, but in that case it should be 0 which is the default integer value.
Any ideas?
Perhaps it is possible to combine two iterators, one for each map?
Kind regards,
Guus Leijsten
You can use the regular for-loop for this :
#include <iostream>
#include <map>
int main(int argc, char* argv[]) {
std::map<int, std::string> m1, m2;
m1.insert({15, "lala"});
m1.insert({10, "hey!"});
m1.insert({99, "this"});
m2.insert({50, "foo"});
m2.insert({51, "bar"});
for(auto it_m1 = m1.cbegin(), end_m1 = m1.cend(),
it_m2 = m2.cbegin(), end_m2 = m2.cend();
it_m1 != end_m1 || it_m2 != end_m2;)
{
if(it_m1 != end_m1) {
std::cout << "m1: " << it_m1->first << " " << it_m1->second << " | ";
++it_m1;
}
if(it_m2 != end_m2) {
std::cout << "m2: " << it_m2->first << " " << it_m2->second << std::endl;
++it_m2;
}
}
return EXIT_SUCCESS;
}
Note that because you want to iterate over maps of different size, you have to use the || operator in loop condition. The direct consequence is that you cannot increment in the last part of the for-loop, as one of the iterator may be invalid at that time (and lead to a segmentation fault).
You have to check iterator validity inside the loop and increment it when it's valid, as shown in the sample above.
My std::map has pair of 'unique key' and 'unique value'. I usually find a key for a value and find a value for a key as well as.
I already know the method which by using std::find_if + lambda, however I want to know if there are any better ways.
After searching, I've found this article and I've learned how to use `std::binary_function'.
Using both approach, I've checked 'elapsed time'.
This is my code.
typedef int USER_ID;
typedef std::string USER_NICK_NAME;
typedef std::map<USER_ID, USER_NICK_NAME> USER_MAP;
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type, typename T::mapped_type, bool>
{
public:
bool operator() (typename T::value_type &pair, typename T::mapped_type i) const
{
return pair.second == i;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
USER_MAP user_map;
string nick_prefix = "test";
//make test map
for (int i = 0; i < 100000; i++)
{
std::ostringstream stream;
stream << i;
user_map.insert(USER_MAP::value_type(i, nick_prefix + stream.str()));
}
const USER_NICK_NAME nick_name = "test99999";
clock_t t;
//Method 1 : using find_if + lambda
cout << "Method 1 : using find_if + lambda" << endl;
t = clock();
auto it = std::find_if(user_map.begin(), user_map.end(), [&](const USER_MAP::value_type& user)
{
return nick_name == user.second;
});
if (it != user_map.end())
{
cout << "found nickname " << nick_name.c_str() << ", at index " << it->first << endl;
}
t = clock() - t;
cout << "elapsed " << ((float)t)/CLOCKS_PER_SEC << " seconds" << endl;
cout << endl << endl;
//Method 2 : using find_if + binary_function
cout << "Method 2 : using using find_if + binary_function" << endl;
t = clock();
it = std::find_if(user_map.begin(), user_map.end(), std::bind2nd(map_data_compare<USER_MAP>(), nick_name));
if (it != user_map.end())
{
cout << "found nickname " << nick_name.c_str() << ", at index " << it->first << endl;
}
t = clock() - t;
cout << "elapsed " << ((float)t)/CLOCKS_PER_SEC << " seconds" << endl;
return 0;
}
In my machine, Method 1 is always faster than Method2.
This is test result console.
So, My question is,
In my situation(I mean searching map by value), find_if + lambda is the best way?
(Unfortunately, I can't use boost library.)
When I use std::binary_function?
I know that in C++ 11, `std::binary_function' has bee deprecated. Could I know the reason?
Thank you for your time to view this thread and for trying to help.
In my situation(I mean searching map by value), find_if + lambda is the best way?
It's certainly the neatest way (assuming you can't use a second map, or perhaps a boost-style multi-index map, to find values quickly). In principle, using an equivalent functor and/or bind shouldn't be significantly slower, the only difference here being that nick_name is captured by value rather than reference; perhaps you haven't enabled optimisations, or perhaps your compiler doesn't optimise bind2nd as well as one might hope.
When I use std::binary_function?
Historically, you'd inherit from it to inject type aliases (first_argument_type, second_argument_type and result_type) into your functor, if you didn't feel like defining them yourself. These were sometimes required, for example when using adapters like bind2nd (which are also deprecated) to create a new functor based on your one.
I know that in C++ 11, std::binary_function has bee deprecated. Could I know the reason?
The types it defines are neither necessary or sufficient for the new-style variadic adapters like bind, so it no longer does anything useful.
Assuming you have the C++1y feature of transparent comparisons, you can create a std::set of std::map::iterator that is sorted by the .second field, and make the comparator transparent so you can do lookups in it by the type of the .second field.
But that is unlikely.
If you do not have this, and you can make your value field (reasonably) cheap to copy, you can make a std::map or std::unordered_map from the value field to iterators into the std::map. This assumes you need both lookup and order in the main map.
If you do not need order, stop using map:
typedef std::unordered_map< int, std::string > main_map;
typedef std::unordered_map< std::string, int > backwards_map;
then wrap the above in some boilerplate to keep the two in sync.
Note that unordered_map iterators are non-persistent. std::map iterators are highly persistent. So the backwards map is different for the double-unordered case.
As for binary_function and bind2nd, it is deprecated.
I ran into a strange problem. I have a vector<pair<bool, int>> from which I need to read (and possibly write) only the vector elements for which the boolean value of the pair is true. I am using boost range filter and reverse adaptors to do that.
However, I noticed that the order of the adaptors, ie whether I use reversed | filtered or filtered | reversed produces different results. In fact, when I use filtered | reversed then when I use an iterator to the transformed range to change the boolean value of the pair, then the iterator after the change points to a different vector element. This does not happen when I use reversed | filtered. Below is the code demonstrating the issue. Any ideas as to why this is happening are much appreciated!
#include <boost/range/adaptors.hpp>
#include <vector>
#include <utility>
#include <iostream>
using namespace boost::adaptors;
using container_type = std::vector<std::pair<bool,int>>;
struct to_include {
bool operator()(const std::pair<bool,int>& x) {
return x.first;
}
};
int main() {
container_type container;
/* element0: 1, 1 */
/* element1: 1, 2 */
/* element2: 1, 3 */
for(size_t i=0; i!=3; ++i) container.push_back(std::make_pair(true, i+1));
container_type container_cpy = container;
/* filter and then reverse */
auto fr = container | filtered(to_include()) | reversed;
auto fr_it1 = fr.begin();
auto fr_it2 = std::next(fr_it1);
fr_it2->first = false;
std::cout << "FILTER AND THEN REVERSE\n";
std::cout << fr_it2->first << " " << fr_it2->second << '\n'; /* prints (1,1) instead of (0,2) */
/* reverse and then filter */
auto rf = container_cpy | reversed | filtered(to_include());
auto rf_it1 = rf.begin();
auto rf_it2 = std::next(rf_it1);
rf_it2->first = false;
std::cout << "\nREVERSE AND THEN FILTER\n";
std::cout << rf_it2->first << " " << rf_it2->second << '\n'; /* prints (0,2) */
return 0;
}
This is a subtle issue. The point here is that after you modify the element pointed to by fr_it2, you also implicitly modify fr_it1 because fr is a lazy view on the original range. This means that the transformed filter needs to be recomputed. This is a very non-intuitive property, because for eager STL ranges, modifications through iterators don't modify the iterators themselves, but for lazy ranges this is no longer true!
In fact, if you print the entire fr and rf ranges using "fresh" iterators, you will see that their contents are in fact the same.
fr_it2->first = false;
for (auto e : fr) std::cout << e.first << e.second << ";"; // prints 13;11
...
rf_it2->first = false;
for (auto e : rf) std::cout << e.first << e.second << ";"; // prints 13;11
Live Example 1. So in fact the middle element is indeed deleted!
I think you should not modify elements through iterators into the adapated range, but rather through iterators into your primary container, like this:
auto fr_it1 = container.begin();
...
auto rf_it1 = container_cpy.begin();
Live Example 2.
If you do that, you get consistent results that show "0 2" for both approaches.
Hi I am stuck in between the concept of Map in STL Library/C++.
int arr[] = {10,15,14,13,17,15,16,12,18,10,29,24,35,36};
int n = sizeof arr / sizeof *arr;
map<int, bool> bst;
map<int, bool>::iterator it;
vector<int> median_output;
const int k = 5;
for (int i = 0; i < k; ++i) {
bst.insert(make_pair(arr[i], true));
}
for (it = bst.begin(); it != bst.end(); it++) {
cout << (*it).first << " ";
}
Now when i printed this map, it got printed in sorted Order. Now is there any simplest way to find the middle of this map.....
Need to find the median of a bigger problem... So trying to implement balanced binary search tree..
map is a balanced search tree. To find it's middle - find it's size, and iterate from the begin() for half it's size - that will be the middle. Something like this:
for (it = bst.begin(), int middle = 0; middle < bst.size()/2; it++, middle++) {
cout << (*it).first << " ";
}
// now after the loop it is the median.
If you use map to sort things - then it's an overkill, IMHO. You can do it much more effectively with an array (or vector), and then finding the middle will be trivial as well. map is used for accessing data by key, not just sorting.
With the code shown you are abusing the map to sort the keys.
You can get much more performance, avoiding full sort and copy:
const int len = 14;
const int a[len] = {10,15,14,13,17,15,16,12,18,10,29,24,35,36};
std::nth_element( a, a+len/2, a+len );
std::cout << "Median: " << a[len/2] << std::endl;
If you prefer to use STL containers, your code would look like this (assuming a container with random access iterators):
std::vector<int> v( a, a+len );
std::nth_element( v.begin(), v.begin()+len/2,v.end() );
std::cout << "Median: " << v[len/2] << std::endl;
std::map might not be the best container for locating the median. But this will do the trick pretty simply:
it = bst.begin();
advance( it, bst.size() / 2);
cout << endl << "median: " << it->first << endl;
std::maps can not give you medians in one shot. If you want medians you need to use this algorithm.