我有map<K, V>
,我想使用unique_copy
将值放入vector<V>
。
我尝试了这个,但它不起作用:
#include <iostream>
#include <map>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
using namespace placeholders;
int main() {
map<std::string, int> mp;
mp["1"] = 1;
mp["2"] = 2;
mp["3"] = 3;
mp["4"] = 3;
mp["5"] = 3;
mp["6"] = 4;
mp["7"] = 2;
vector<int> vec;
unique_copy( mp.begin(), mp.end(),
back_inserter(vec),
bind(&map<std::string, int>::value_type::second, _1) );
for( auto& i : vec )
cout<< i <<" ";
}
预期输出: 1 2 3 4 2
所有Google搜索都会返回使用transform
的方法,但我需要unique_copy
。有没有办法让这项工作?
答案 0 :(得分:6)
没有办法直接执行此操作,因为map迭代器的值类型和矢量迭代器不兼容。你真的需要某种变换,或者迭代器适配器。
您可以使用boost::transform_iterator
:
auto getValue = [](const std::map<std::string, int>::value_type &pair) { return pair.second; };
unique_copy(
boost::make_transform_iterator(mp.begin(), getValue),
boost::make_transform_iterator(mp.end(), getValue),
back_inserter(vec)
);
如果你不能使用Boost,你必须自己编写这样的迭代器适配器:
template <class T_PairIterator>
struct SecondIterator
{
typedef T_PairIterator PairIterator;
typedef typename std::iterator_traits<PairIterator>::iterator_category iterator_category;
typedef typename std::iterator_traits<PairIterator>::value_type::second_type value_type;
typedef typename std::iterator_traits<PairIterator>::difference_type difference_type;
typedef value_type *pointer;
typedef value_type &reference;
PairIterator it;
SecondIterator() {}
explicit SecondIterator(PairIterator it) : it(it) {}
pointer operator-> () const { return &it->second; }
reference operator* () const { return it->second; }
SecondIterator& operator++ () { ++it; return *this; }
SecondIterator operator++ (int) { SecondIterator ret(*this); ++it; return ret; }
};
template <class T>
bool operator== (const SecondIterator<T> &lhs, const SecondIterator<T> &rhs)
{ return lhs.it == rhs.it; }
template <class T>
bool operator!= (const SecondIterator<T> &lhs, const SecondIterator<T> &rhs)
{ return !(lhs == rhs); }
template <class T>
SecondIterator<T> makeSecondIterator(const T &it)
{ return SecondIterator<T>(it); }
然后您可以像这样使用它:
unique_copy(
makeSecondIterator(mp.begin()),
makeSecondIterator(mp.end()),
back_inserter(vec)
);
当然,适配器可以更通用(也许可用于first
),和/或更多封装(it
不需要公开);它还需要对常量函数进行适当的处理。但是上面应该足以让你有这个想法。
答案 1 :(得分:1)
正如 Vijay 在评论中所说,你可以使用std::set
代替向量(如果你不需要向量的各个特征,当然,就像连续存储一样,定制订购或随机访问)。在这种情况下,std::set::insert
已经处理了删除重复内容的问题,您可以按照您尝试的方式使用std::transform
:
std::set<int> set;
std::transform( mp.begin(), mp.end(), std::inserter(set),
bind(&std::map<std::string, int>::value_type::second, _1) );
关于修改后的问题
如果值已经在集合中,那么std :: set将不会插入值,但是我 不要连续重复。
嗯,在这种情况下,只需在向量中执行正常复制(即std::transform
)并在之后执行std::unique
,这将删除所有连续的重复项:
std::transform( mp.begin(), mp.end(), std::inserter(vec),
bind(&std::map<std::string, int>::value_type::second, _1) );
vec.erase( std::unique(vec.begin(), vec.end()), vec.end() );
答案 2 :(得分:0)
std::copy
后面跟vec.erase( std::unique( vec.begin(), vec.end() ), vec.end() );
最终会与unique_copy
位于同一位置。它可能在中间使用额外的内存(在删除冗余条目之前),但非常干净。
手动复制是另一种选择:
if(!src.empty()){
auto* prev = &src.begin()->second;
dest.push_back(*prev);
for(auto it = std::next(src.begin()); it != sec.end(); ++it){
if (it->second == *prev) continue;
prev=&it->second;
dest.push_back(*prev);
}
}
注意事项prev
在技术上是多余的(使用dest.back()
而不是*prev
,除非push_back
- 使用->second
)。