Iterate over vector using first value of std::pair only - c++

I have a std::vector as described below:
std::vector<std::pair<int, const char*>> matrix;
This vector has the following values (for e.g.): values (as an example)
These values can be access here as follows:
matrix[0] = [0,Hello] // pseudo code (only showing values inside)
matrix[1] = [0,Fox] // pseudo code (only showing values inside)
matrix[2] = [1,Red] // pseudo code (only showing values inside)
I am iterating through the contents of the vector read the values, by doing this:
for (std::vector<std::pair<int, const char*>>::iterator it = matrix.begin(); it != matrix.end(); ++it)
{
std::pair<int, const char*> v_temp = *it;
std::cout << v_temp.first;
std::cout << v_temp.second;
}
Now, what this is doing is iterating from the first element of vector to the end element of the vector. What I want to do, is iterate only on the first elements (i.e. int values). So, from the tabular image I have attached, this current code will loop for [ row x column ] [ 9 x 2] = 18 times. What I want it for it to iterate only 9 times [ rows ] and not consider columns at-all.
How can I do that?

There are a lot of ways to skin this particular cat. One would be to use std::transform:
std::transform(v.begin(), v.end(),
std::ostream_iterator<int>(std::cout, "\t"),
[](auto const &p) { return p.first; });

TL;DR boost::transform_iterator is your friend.
Example taken from What is the equivalent of boost::make_transform_iterator in the standard library?:
// I couldn't realize how to specialize type of std::get, so I need this helper.
inline int tuple_fst(const std::tuple<int, const char*> &x) {
return x.first;
}
...
auto beg = boost::make_transform_iterator(matrix.begin(), tuple_fst);
auto end = boost::make_transform_iterator(matrix.end(), tuple_fst);
for (auto it = beg; it != end; ++it) {
std::cout << *it;
}
This is actually a nice question, I don't understand why it is so downvoted. You wanted something like rust's std::iter::Map or Haskell's map. Unfortunately, in C++ things get a bit more ugly if you want high level iterator functions.

Your loop iterates over the rows of the matrix. Element iof matrix is the "row" at index i (in this case a std::pair<int, const char*>. So if you have pushed back 9 pairs then the loop will iterate 9 times.
FYI: You can simplify your code using the C++ feature auto:
for(auto it = matrix.begin(); it != matrix.end(); ++it){
auto v_temp = *it;
std::cout << v_temp.first;
std::cout << v_temp.second;
}
A further simplification is
for(const auto& v_temp : matrix)
{
std::cout << v_temp.first;
// ...
}
You can use the C++11 for-range based loop for any object on which the compiler can call begin() and end() on.

Related

Pick random element subset from map

I have a map of elements:
std::map<char,int> values;
values['a']=10;
values['b']=30;
values['c']=50;
values['d']=70;
values['e']=90;
values['f']=100;
values['g']=120;
So I need to pick N elements from values as a map of pairs preferably (output format as well as input format).
I tried other different solutions from stackoverflow but they mostly applicable for vector not for any type of C++ 11 container or looks too complicated for me.
I need some more effective way than just random_shuffle which actually mutates C++ container.
Also it would be nice if this function would be applicable for any type of C++ container.
You could copy the keys of the std::map<char, int> into an std::vector<char>. Then, shuffle this vector with std::random_shuffleX. Finally, return num elements of the map: the ones whose keys are the num last keys in the vector:
std::vector<std::pair<char, int>> pick_random(const std::map<char, int>& m, size_t num)
{
std::vector<char> keys;
keys.reserve(m.size());
// copy the map's keys
std::transform(m.begin(), m.end(), std::back_inserter(keys),
[](const std::pair<const char, int>& p) {
return p.first;
}
);
// shuffle the keys
std::random_shuffle(keys.begin(), keys.end());
// number of elements to pick
num = std::min(num, m.size());
std::vector<std::pair<char, int>> res;
res.reserve(num);
// pick num elements
std::generate_n(std::back_inserter(res), num,
[&keys, &m]() {
auto it = m.find(keys.back());
keys.pop_back();
return *it;
}
);
return res;
}
The idea is to randomly shuffle the elements in the vector containing the keys (i.e., keys). Therefore, you shuffle the keys, which map to elements in the map. You use these randomly shuffled keys to obtain elements from the map in a random way.
Xor std::shuffle insted since std::random_shuffle has been deprecated in C++14 and removed in C++17.
std::sample is C++17, but you can implement it yourself pretty easily in C++11. For example, for forward iterators (and std::map iterator does satisfy this requirement) typical implementation relies on the selection sampling algorithm. Its description can be found in Vol.2. of Knuth's TAOCP, p.142.
Simplest implementation:
template<class Forw_it, class Out_it, class URBG>
void sample(Forw_it first, Forw_it last, Out_it out, std::ptrdiff_t n, URBG&& g) {
auto sz = std::distance(first, last);
n = std::min(n, sz);
while (n != 0) {
std::uniform_int_distribution<std::ptrdiff_t> d(0, --sz);
if (d(g) < n) {
*out++ = *first;
--n;
}
++first;
}
}
Usage example:
std::map<char, int> values;
// ... assign values ...
std::vector<std::pair<char, int>> vec;
sample(values.begin(), values.end(), std::back_inserter(vec), 4, std::mt19937{});
for (auto p : vec)
std::cout << p.first << ' ' << p.second << '\n';
Sample output:
b 30
e 90
f 100
g 120
Full demo

Converting const auto & to iterator

A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.

operation on 2 dimensional hash map C++

I am currently working on a coding question:
Given an array of strings, return all groups of strings that are anagrams.
For example, Given:
{asch, scah, bva, vba, soa}
return
{ {asch, scah}, {bva, vba}, {soa}}
To solve this question less than O(n^2) time, we should firstly sort each word, and group the sorted words in one set, if the sorted words are the same.
I wanted to use two-dimensional hashmap.
map<string, map<int,string>> container;
to use this two-dimensional hashmap, the first key is the sorted word, the second key is its index in the original sequence, and the value is the original word.
for(int i=0; i<sequence.size();i++)
{
string original_word = sequence[i];
string sorted_word = original_word;
sort(sorted_word.begin(),sorted_word.end());
container[sorted_word][i] = original_word;
}
After this loop, I believe all the anagrams which must have the same sorted_word, will be grouped into the first level of hashmap.
My question is, how should I write the code in order to get the set which has the same sorted_word?
Can I do
for( iterator itr = container.begin(); itr != container.end(); itr++)
{
auto grouped_words = itr.second(); // what is the data type of grouped_word here?
}
correct me if there is anything wrong. Thanks.
I think there's a mistake here:
vector<string, vector<int,string>> container; // ???
As in your question you speak about hash maps, I suppose that you meant:
unordered_map<string, unordered_map<int,string>> container;
In this case, you can use the result as follows:
for( auto itr = container.begin(); itr != container.end(); itr++)
{
auto &grouped_words = itr->second; // prefer a reference
cout << itr->first<<": ";
for (auto &x : grouped_words) {
cout << "\t" << x.first << ":"<< x.second<<endl;
}
}
Here a live demo.
Edit: grouped_words is (here) a reference to an unordered_map<int, string>

Moving object to front of vector c++

I have a vector<Suggestions> finalSuggestions that contains a string word and some int num.
If this word meets some condition, I want to move that object to the front of the vector, removing it from wherever it was.
I am able to insert to the beginning of the list with vector::insert
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
break;
}
}
But I do not know how to remove it from where it is in the list.
For example, for some arbitrary vector { 1,2,3,4,50,6,7,8,9 },
if 50 meets the criteria, move it to the front of the list and delete it from where it was, returning { 50,1,2,3,4,6,7,8,9 }. The code above returns { 50,1,2,3,4,50,6,7,8,9 }
I was looking into vector::erase, but I'm having problems, and its taking longer than it should.
I envision a simple solution (but this obviously doesn't work)
for (auto &x: finalSuggestions) {
if ( double((x.num)/(topword.num)) < 50)
{
finalSuggestions.insert(finalSuggestions.begin(),x);
finalSuggestions.erase(x);
break;
}
}
I read up on the erase-remove idiom (here is my implementation):
finalSuggestions.erase( remove( begin(finalSuggestions), end(finalSuggestions), x ), end(finalSuggestions) );
but am getting an error that I don't understand:
In instantiation of '_FIter std::remove(_FIter, _FIter, const _Tp&) [with _FIter = __gnu_cxx::__normal_iterator<Suggestion*, std::vector<Suggestion> >; _Tp = Suggestion]':|
Use std::rotate. It's a lot faster than deleting and reinserting.
Eg:
for (auto it = finalSuggestions.begin(), lim = finalSuggestions.end();
it != lim;
++it) {
if (it->num < 50 * topword.num) {
std::rotate(finalSuggestions.begin(), it, it + 1);
break;
}
}
Even better, as #JerryCoffin suggests in a comment, use std::find_if to find the pivot:
auto pivot = std::find_if(finalSuggestions.begin(),
finalSuggestions.end(),
[&topword](const Suggestions& s) -> bool {
return s.num < 50 * topword.num;
});
if (pivot != finalSuggestions.end()) {
std::rotate(finalSuggestions.begin(), pivot, pivot + 1);
}
For vector::erase you need an iterator, so range-based for can't be used. Use simple for loop instead. First erase an element, and then insert it, because insert invalidates iterators:
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (some_condition(*it)) {
auto x = *it; // or std::move(*it)
finalSuggestions.erase(it);
finalSuggestions.insert(finalSuggestions.begin(), x /* or std::move(x) */);
break;
}
}
Using std::move will allow you to move an element around instead of copying it, which may save you some cycles.
Your iterator makes it difficult to know the position of the element in question. You might want to try using a standard for iterator which allows access to the position (used by std::vector::erase)
int len=finalSuggestions.size();
for (int i=0, ; i<len; ++i) {
// Save a copy of the item
auto item = finalSuggestions.at(i);
if (double((item.num)/(topword.num)) < 50) {
// Erase the item in the list
finalSuggestions.erase(i);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
... or using a std::iterator ...
for (auto it = finalSuggestions.begin(); it != finalSuggestions.end(); ++it) {
if (double((*it->num)/(topword.num)) < 50) {
// Save a copy of the item
auto item = *it;
// Erase the item in the list
finalSuggestions.erase(it);
// Add the copy of the item back in at the front
finalSuggestions.insert(finalSuggestions.begin(), item);
break;
}
}
std::vector objects use contiguous memory for their elements, which means actually moving memory around during altering of the container. If you are going to be moving elements around you may want to look into std::list or std:deque. The definition of these containers are nearly identical (read: drop in replacements) to each other making it fairly straight-forward to replace them.
Suggestion:
The std::deque is designed for optimal insertions at both the beginning and the end of the container. Taken from the site cplusplus.com:
... they provide a functionality similar to vectors, but with efficient insertion and deletion of elements also at the beginning of the sequence, and not only at its end. But, unlike vectors, deques are not guaranteed to store all its elements in contiguous storage locations: ...
Anton's answer is correct. However if you do this sort of thing a lot you should consider a different data structure. Both the erase and the insert are O(N) operations, where N is the size of the vector. A list would be better if this is a common operation.
It is functionally equivalent to Anton's answer, but I would use std::find_if to get the an iterator to the element you are looking for instead of a loop.
//add #include <algorithm> to your source file
auto result = std::find_if(finalSuggestions.begin(), finalSuggestions.end(), condition_func);
if(result != finalSuggestions.end())
{
auto resultValue = *result;
finalSuggestions.erase(result);
finalSuggestions.insert(finalSuggestions.begin(), resultValue);
}
condition_func should be a function returning bool that takes a parameter matching the type of the elements in your vector (in this case, Suggestion):
bool condition_func(Suggestion elementValue) { /*condition here*/ }
More information on find_if is available here.
Maybe using std::iter_swap could solve your problem.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main () {
vector<int> myvector{};
for(int io{}; io<7; ++io) myvector.push_back(io+1);
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
cout << ' ' << *it;
cout << '\n';
iter_swap(myvector.begin(),myvector.begin()+2);//exchange the third element with the first.
cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}

How can I skip elements in a range-based for loop based on 'index'?

Is there a way to access the iterator (I suppose there's no loop index?) in a C++11 range-based for loop?
Often we need to do something special with the first element of a container and iterate over the remaining elements. So I'm looking for something like the c++11_get_index_of statement in this pseudo-code:
for (auto& elem: container)
{
if (c++11_get_index_of(elem) == 0)
continue;
// do something with remaining elements
}
I'd really like to avoid going back to old-style manual iterator handling code in that scenario.
Often we need to do something special with the first element of a
container and iterate over the remaining elements.
I am surprised to see that nobody has proposed this solution so far:
auto it = std::begin(container);
// do your special stuff here with the first element
++it;
for (auto end=std::end(container); it!=end; ++it) {
// Note that there is no branch inside the loop!
// iterate over the rest of the container
}
It has the big advantage that the branch is moved out of the loop. It makes the loop much simpler and perhaps the compiler can also optimize it better.
If you insist on the range-based for loop, maybe the simplest way to do it is this (there are other, uglier ways):
std::size_t index = 0;
for (auto& elem : container) {
// skip the first element
if (index++ == 0) {
continue;
}
// iterate over the rest of the container
}
However, I would seriously move the branch out of the loop if all you need is to skip the first element.
Boost provides a nice succinct way to do this:
std::vector<int> xs{ 1, 2, 3, 4, 5 };
for (const auto &x : boost::make_iterator_range(xs.begin() + 1, xs.end())) {
std::cout << x << " ";
}
// Prints: 2 3 4 5
You can find make_iterator_range in the boost/range/iterator_range.hpp header.
How about using a simple for loop with iteratos:
for(auto it = container.begin(); it != container.end(); it++)
{
if(it == container.begin())
{
//do stuff for first
}
else
{
//do default stuff
}
}
It's not range based, but it's functional.
In case you may still want to use the range loop:
int counter = 0;
for(auto &data: container)
{
if(counter == 0)
{
//do stuff for first
}
else
{
//do default stuff
}
counter++;
}
No, you can't get the iterator in a range-based for loop (without looking up the element in the container, of course). The iterator is defined by the standard as being named __begin but this is for exposition only. If you need the iterator, it is intended that you use the normal for loop. The reason range-based for loop exists is for those cases where you do not need to care about handling the iteration yourself.
With auto and std::begin and std::end, your for loop should still be very simple:
for (auto it = std::begin(container); it != std::end(container); it++)
When iterating over elements, always prefer to use an algorithm, and use a plain for loop only if none of the algorithms fit.
Picking the right algorithm depends on what you want to do with the elements... which you haven't told us.
If you want to skip the first element, dump example:
if (!container.empty()) {
for_each(++container.begin(), container.end(), [](int val) { cout << val; });
}
There is no way of knowing how far an element is within the container without having an iterator, pointer or an intrusive index. Here's a simple way of doing it:
int index= 0;
for (auto& elem: container)
{
if (index++ == something)
continue;
// do something with remaining elements
}
If you want to skip the first element, another way is to use a std::deque and pop_front the first element. Then you can do your ranged for loop with the container as usual.
When I need to do something like this on a random access container, my habit is to iterate over the indexes.
for( std::size_t i : indexes( container ) ) {
if (i==0) continue;
auto&& e = container[i];
// code
}
the only tricky part is writing indexes, which returns a range of what boost calls counting iterators. Creating a basic iterable range from iterators is easy: either use boost's range concept, or roll your own.
A basic range for an arbitrary iterator type is:
template<typename Iterator>
struct Range {
Iterator b; Iterator e;
Range( Iterator b_, Iterator e_ ):b(b_), e(e_) {};
Iterator begin() const { return b; }
Iterator end() const { return e; }
};
which you can gussy up a bunch, but that is the core.
I would try to avoid using iterators, because the idea of a range-based for loop is to get rid of them. As of C++20, to skip the first element in your container, I would take one of the following approaches. I also include, for the sake of completeness, how to handle the first element separately:
Handling the first element outside the loop
You can use container.front() which exists for all sequence containers to access the first element. However, you must make sure that the container is not empty to avoid a segmentation fault. Then, to skip the first element (or more) in the loop, you can use the range adapter std::views::drop from the Ranges library. All together it looks as follows:
std::vector<int> container { 1, 2, 3 };
if(!container.empty()) {
// do something with first element
std::cout << "First element: " << container.front() << std::endl;
}
for (auto& elem : container | std::views::drop(1)) {
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
Instead of container.front() you can also use another range-based for loop together with the range adapter std::views::take(1). The advantage of take() and drop() is that they work safely even if their arguments exceed the count of elements in your container.
Handling the first element inside the loop
You can use an init-statement in a range-based for loop to define a Boolean flag (or even a counter). This way, the flag is visible only within the scope of the loop. You can use the flag inside the loop as follows:
std::vector<int> container { 1, 2, 3 };
for(bool isFirst(true); auto& elem : container) {
if(isFirst) {
// do something with first element
std::cout << "First element: " << elem << std::endl;
isFirst = false;
continue;
}
// do something with remaining elements
std::cout << "Remaining element: " << elem << std::endl;
}
Output for both approaches shown:
First element: 1
Remaining element: 2
Remaining element: 3
Code on Wandbox