Given a multimap<A,B> M what's a neat way to create a vector<B> of all values in M with a specific key.
e.g given a multimap how can I get a vector of all strings mapped to the value 123?
An answer is easy, looping from lower->upper bound, but is there a neat loop-free method?
Here's the way to do it STL style :
// The following define is needed for select2nd with DinkumWare STL under VC++
#define _HAS_TRADITIONAL_STL 1
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <map>
#include <iterator>
#include <iostream>
using namespace std;
void main()
{
typedef multimap<string, int> MapType;
MapType m;
vector<int> v;
// Test data
for(int i = 0; i < 10; ++i)
{
m.insert(make_pair("123", i * 2));
m.insert(make_pair("12", i));
}
MapType::iterator i = m.lower_bound("123");
MapType::iterator j = m.upper_bound("123");
transform(i, j, back_inserter(v), select2nd<MapType::value_type>());
copy(v.begin(), v.end(), ostream_iterator<int>(cout, ","));
}
Let's go lambda
given: multimap<A,B> M
requested: vector<B> (of all values in M with a specific key 'a'.)
method:
std::pair<M::iterator, M::iterator> aRange = M.equal_range('a')
std::vector<B> aVector;
std::transform(aRange.first, aRange.second,std::back_inserter(aVector), [](std::pair<A,B> element){return element.second;});
System environment:
compiler: gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 (with -std=c++11)
os: ubuntu 16.04
Code example:
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <iostream>
int main()
{
typedef std::multimap<std::string, int> MapType;
MapType m;
std::vector<int> v;
/// Test data
for(int i = 0; i < 10; ++i)
{
m.insert(std::make_pair("123", i * 2));
m.insert(std::make_pair("12", i));
}
std::pair<MapType::iterator,MapType::iterator> aRange = m.equal_range("123");
std::transform(aRange.first, aRange.second, std::back_inserter(v), [](std::pair<std::string,int> element){return element.second;});
for(auto & elem: v)
{
std::cout << elem << std::endl;
}
return 0;
}
You need a loop anyway. All "loop-free" methods just abstract the loop away.
#include <map>
#include <vector>
#include <algorithm>
#include <ext/functional>
using namespace std;
int main () {
multimap<int, double> mm;
mm.insert(make_pair(1, 2.2));
mm.insert(make_pair(4, 2.6));
mm.insert(make_pair(1, 9.1));
mm.insert(make_pair(1, 3.1));
vector<double> v;
transform(mm.lower_bound(1), mm.upper_bound(1),
back_inserter(v), __gnu_cxx::select2nd<pair<int, double> >());
// note: select2nd is an SGI extension.
for (vector<double>::const_iterator cit = v.begin(); cit != v.end(); ++ cit)
printf("%g, ", *cit); // verify that you've got 2.2, 9.1, 3.1
return 0;
}
template <class Key, class Val>
vector<Val>& getValues(multimap<Key, Val>& multi, Key& key)
{
typedef multimap<Key, Val>::iterator imm;
static vector<Val> vect;
static struct
{
void operator()(const pair<Key, Val>& p) const
{
vect.push_back(p.second);
}
} Push;
vect.clear();
pair<imm, imm> range = multi.equal_range(key);
for_each(range.first, range.second, Push);
return vect;
}
This is a bit contrived because of your 'no loop' requirement.
I prefer:
template <class Key, class Val>
vector<Val> getValues(multimap<Key, Val>& map, Key& key)
{
vector<Val> result;
typedef multimap<Key, Val>::iterator imm;
pair<imm, imm> range = map.equal_range(key);
for (imm i = range.first; i != range.second; ++i)
result.push_back(i->second);
return result;
}
You could initialise the vector by giving it two iterators, like this:
std::multimap<std::string, std::string> bar;
...
std::vector<pair<string,string> > foo(bar.lower_bound("123"), bar.upper_bound("123"));
but that would give you a vector of pairs (ie, with both the key and value).
Another option would be to use std::copy with something like a back_inserter, which is another way to hide the loop, but with the same downside as above.
std::copy(bar.lower_bound("123"), bar.upper_bound("123"), std::back_inserter(foo));
This would append the elements (if any) to the vector foo.
For extracting the values only, I can't think of any way but to loop over the results as I'm not aware of a standard way to get only the value out of a range.
Just some addenda to the other answers hereā¦
std::mem_fn (from #include <functional>) can be used as a shorthand for the transform operator:
// previously we might've used this longhand
[](pair<int,string> element){return element.second;}
And we can use vector::resize and std::distance to allocate space for the vector in one go, rather than repeatedly resizing it with back_inserter.
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <functional>
#include <iterator>
#include <iostream>
using namespace std;
typedef multimap<int, string> MapType;
int main()
{
MapType multimap;
vector<string> valuesForKey123;
multimap.emplace(0, "red");
multimap.emplace(123, "hello");
multimap.emplace(123, "world");
multimap.emplace(0, "herring");
MapType::iterator lower{multimap.lower_bound(123)};
MapType::iterator upper{multimap.upper_bound(123)};
valuesForKey123.resize(distance(lower, upper));
transform(
lower,
upper,
valuesForKey123.begin(),
mem_fn(&MapType::value_type::second));
copy(
valuesForKey123.begin(),
valuesForKey123.end(),
ostream_iterator<string>(cout, " "));
}
// outputs "hello world "
Related
I am trying to remove a tuple from a vector that matches the input string.
vector< tuple<string, int, int> > goals;
So, if the string matches with the input string x, I want it to be removed.
void remove_goal(const string& x){
goals.erase(remove(goals.begin(), goals.end(), x));
}
But I am getting this error
error: invalid operands to binary expression ('std::__1::tuple<std::__1::basic_string<char>, std::__1::basic_string<char>, int>' and 'const std::__1::basic_string<char>')
if (!(*__i == __value_))
~~~~ ^ ~~~~~~~~
Here's an example that demonstrates a fairly efficient way to delete std::tuples from a std::vector that match a criteria.
It uses the aptly named erase/remove idiom.
#include <algorithm>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
int main() {
std::vector<std::tuple<std::string, int, int>> v;
v.push_back(std::make_tuple("Hello", 1, 1));
v.push_back(std::make_tuple("Cruel", 2, 2));
v.push_back(std::make_tuple("World.\n", 3, 3));
v.erase(std::remove_if(v.begin(), v.end(),
[](auto s) { return std::get<0>(s) == "Cruel"; }),
v.end());
for (auto i : v) {
std::cout << std::get<0>(i) << ' ';
}
}
Output:
Hello World.
As you can see, you were on the right track. But you cannot directly compare a std::string to a std::tuple just because the std::tuple contains a std::string. That logic doesn't really make sense nor does it extend to other examples.
This addresses your edit where the erase occurs in a function:
#include <algorithm>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
void remove_from(std::vector<std::tuple<std::string, int, int>>& v,
const std::string& val) {
v.erase(std::remove_if(v.begin(), v.end(),
[&val](auto s) { return std::get<0>(s) == val; }),
v.end());
}
int main() {
std::vector<std::tuple<std::string, int, int>> v;
v.push_back(std::make_tuple("Hello", 1, 1));
v.push_back(std::make_tuple("Cruel", 2, 2));
v.push_back(std::make_tuple("World.\n", 3, 3));
remove_from(v, "Cruel");
for (auto i : v) {
std::cout << std::get<0>(i) << ' ';
}
}
It works because I'm just making a comparison of values held, and not attempting to modify val at all.
There is no operator== to compare a std::string and a std::tuple<std::string,int,int>. You can use std::remove_if:
#include <vector>
#include <tuple>
#include <string>
#include <algorithm>
#include <iostream>
int main(){
std::vector< std::tuple<std::string, int, int> > goals {{"foo",42,3141}};
std::string x{"foo"};
// x == goals[0]; nope !!
// goals.erase(std::remove(goals.begin(), goals.end(), x)); // nope !!
goals.erase(std::remove_if(goals.begin(),
goals.end(),
[&x](auto t){
return std::get<0>(t) == x;
}));
std::cout << goals.size();
}
Live Demo
Let's say I have one const vector<string> with some elements in it and I'd like to create a const subset of that vector, how can I do that?
Ideally it would be cool if C++ supported something like the code below, unfortunately it doesn't. Anybody know of a work around?
#include <fstream>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
ifstream infile("input.txt"); // contains apple, orange, banana in separate lines
istream_iterator<string> eos;
istream_iterator<string> input(infile);
const vector<string> stuff(input, eos);
const vector<string> a_stuff(stuff.copy_if([](const string& s) { return s[0] == 'a'; }));
return 0;
}
Assuming you want to make a copy that contains the subset, you can use a function and assign the result to a const vector<T>
#include <vector>
#include <algorithm>
#include <iterator>
template <typename T, typename Pred>
std::vector<T> vector_copy_if(std::vector<T> const & vec, Pred && pred)
{
std::vector<T> result;
std::copy_if(vec.begin(), vec.end(), std::back_inserter(result), pred);
return result;
}
int main()
{
std::vector<int> const some_numbers { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::vector<int> const even_numbers = vector_copy_if(some_numbers, [](int x) { return x % 2 == 0; });
}
It is also possible to use a boost::filter_iterator to achieve the same result:
#include <vector>
#include <string>
#include <boost/iterator/filter_iterator.hpp>
using namespace std;
int main()
{
const vector<string> v {"apple", "banana", "orange"};
auto filter= [] (string s){return s.length() > 0 && s[0] == 'a';};
auto start = boost::make_filter_iterator(filter, v.begin(), v.end());
auto end = boost::make_filter_iterator(filter, v.end(), v.end());
const vector<string> cv (start, end);
}
How should I declare std::list(1) with iterators to std::map, which maps std::string to iterators of std::list (1) ? Is it possible?
std::list<std::map<std::string, (1) ???>::iterator>;
std::map<std::string, (1) ???::iterator>;
The reason I want this - FIFO queue with ability to fast remove by key.
One possible solution:
struct decl_t {
typedef std::map< std::string, decl_t > map_t;
typedef std::list< std::pair< int, typename map_t::iterator > > list_t;
list_t::iterator it;
};
For the purpose of a FIFO with the ability to remove by key, I suggest you use unordered_map, as you have no need for order in the map.
Following that, perhaps you could change your cross-referencing scheme. Use a list of strings, and a map mapping strings to iterators of such a list:
#include <unordered_map>
#include <list>
#include <string>
using map_t = unordered_map<string, list<string>::iterator>;
using list_t = list<string>;
For the direction of finding a key in the map once you have an iterator in the list, you need to perform a redundant hash on the name relative to your full iterator-to-iterator scheme, but it is still O(1) (expected). Conversely, your original scheme required logarithmic operations for removal by key, so you're probably still ahead.
To insert a new element, you could do something like this:
map_t map;
list_t list;
list.push_back("koko");
auto it = --list.end();
map["koko"] = it;
Example
#include <unordered_map>
#include <list>
#include <string>
using namespace std;
int main()
{
using map_t = unordered_map<string, list<string>::iterator>;
using list_t = list<string>;
map_t map;
list_t list;
list.push_back("koko");
auto it = --list.end();
map["koko"] = it;
}
Here is ugly, but complete example
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <string>
struct decl_t {
typedef std::map<std::string, decl_t> map_t;
typedef std::list<std::pair<int, typename map_t::iterator>> list_t;
list_t::iterator it;
};
int main(int argc, const char* argv[])
{
decl_t::map_t map;
decl_t::list_t list;
auto list_it = list.emplace(list.end(), 42, decl_t::map_t::iterator());
const auto pair = std::make_pair(std::string("key"), decl_t{list_it});
auto result = map.insert(pair);
assert(result.second);
auto map_it = result.first;
list_it->second = map_it;
std::cout << list_it->second->first << std::endl;
std::cout << map_it->second.it->first << std::endl;
}
typedef map<string,int> mapType;
mapType::const_iterator i;
i = find_if( d.begin(), d.end(), isalnum );
at the '=' i am getting the error:
Error:no operator "=" matches these operands
I know that find_if returns an iterator once the pred resolves to true, so what is the deal?
The documentation for std::find_if
We can only guess at the error as you have only provided half the problem.
Assuming d is mapType and the correct version of isalnum
The problem is that the functor is being passed an object to mapType::value_type (which is how the map and all containers store their value). For map the value_type is actually a key/value pair actually implemented as std::pair<Key,Value>. So you need to get the second part of the object to test with isalnum().
Here I have wrapped that translation inside another functor isAlphaNumFromMap that can be used by find_if
#include <map>
#include <string>
#include <algorithm>
// Using ctype.h brings the C functions into the global namespace
// If you use cctype instead it brings them into the std namespace
// Note: They may be n both namespaces according to the new standard.
#include <ctype.h>
typedef std::map<std::string,int> mapType;
struct isAlphaNumFromMap
{
bool operator()(mapType::value_type const& v) const
{
return ::isalnum(v.second);
}
};
int main()
{
mapType::const_iterator i;
mapType d;
i = std::find_if( d.begin(), d.end(), isAlphaNumFromMap() );
}
If d is a map, then the problem is with your attempted use of isalnum.
isalnum takes a single int parameter, but the predicate called by find_if receives a map::value_type. The types are not compatible, so you need something that adapts find_if to `isalnum. Such as this:
#include <cstdlib>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
typedef map<string,int> mapType;
bool is_alnum(mapType::value_type v)
{
return 0 != isalnum(v.second);
}
int main()
{
mapType::const_iterator i;
mapType d;
i = find_if( d.begin(), d.end(), is_alnum );
}
Say there is a list of integers [1,2,3,4,5] and a map function that multiplies each element with 10 and returns modified list as [10,20,30,40,50] , with out modifying the original list.
How this can be done efficiently in c++.
Here's an example:
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int multiply(int);
int main() {
vector<int> source;
for(int i = 1; i <= 5; i++) {
source.push_back(i);
}
vector<int> result;
result.resize(source.size());
transform(source.begin(), source.end(), result.begin(), multiply);
for(vector<int>::iterator it = result.begin(); it != result.end(); ++it) {
cout << *it << endl;
}
}
int multiply(int value) {
return value * 10;
}
Along the lines of #darids answer, but C++03 (current at the time of original post):
#include <vector>
#include <algorithm>
#include <functional>
std::vector<int> src;
std::vector<int> dst;
std::transform(src.begin(), src.end(),
std::back_inserter(dst),
std::bind1st(std::multiplies<int>(), 10));
If you can use it, probably the best idea is to use a function in the Standard Template Library.
For example, you might want to check out for_each or transform, which basically do just that.
I only post this to illustrate using a functor in transform rather than a global function:
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
struct MulBy : public std::unary_function<int, int>
{
MulBy(int v) : v_(v) {}
int operator()(int lhs) const
{
return lhs * v_;
}
private:
int v_;
};
int main()
{
int incoming[5] = {1, 2, 3, 4, 5};
int result[5] = {0, 0, 0, 0, 0};
transform(&incoming[0], &incoming[5], &result[0], MulBy(10));
copy(&result[0], &result[5], ostream_iterator<int>(cout, " "));
return 0;
}
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
struct MulBy : public std::unary_function<int, int>
{
MulBy(int v) : v_(v) {}
int operator()(int lhs) const
{
return lhs * v_;
}
private:
int v_;
};
int main()
{
vector<int> ListOfNumber;
ListOfNumber.push_back(1);
ListOfNumber.push_back(2);
ListOfNumber.push_back(3);
ListOfNumber.push_back(4);
ListOfNumber.push_back(5);
vector<int> ListResult;
ListResult.resize(ListOfNumber.size());
//Produces a new list
transform(ListOfNumber.begin(),ListOfNumber.end(),ListResult.begin(),MulBy(10));
copy(ListOfNumber.begin(),ListOfNumber.end(),ostream_iterator<int>(cout,"\t"));
//Modifies the original list
transform(ListOfNumber.begin(),ListOfNumber.end(),ListOfNumber.begin(),MulBy(10));
copy(ListResult.begin(),ListResult.end(),ostream_iterator<int>(cout,"\t"));
cin.get();
}
This is my implementation for an array map method, inspired directly from javascript
#include <vector>
#include <functional>
namespace wh{
namespace array{
template<typename T>
std::vector<T> map(const std::vector<T> &vectorToMap, const std::function<T(T)> &functor){
std::vector<T> ret;
for(auto &element: vectorToMap){
ret.push_back(functor(element));
}
return ret;
}
...
}
}
#include <iostream>
int main()
{
//'spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'
std::vector<std::string> words = {"spray", "limit", "elite", "exuberant", "destruction", "present", "windows", "wlol"};}
// and this is how it is used:
words = wh::array::map<std::string>(words, [](auto word){return word+"l";});
for(auto &word: words) std::cout << word << std::endl;
return 0;
}
Maybe this will be useful for someone else, still, if <algorithm> functions are a better approach, go ahead and use them.