使用值对std :: map进行排序


71

我需要std::map按值而不是按键排序。有一个简单的方法吗?

我从下面的线程中得到了一个解决方案:
std :: map按数据排序?
有更好的解决方案吗?

map<long, double> testMap;
// some code to generate the values in the map.

sort(testMap.begin(), testMap.end());  // is there any function like this to sort the map?

3
为什么需要按这种方式排序的地图?您是在尝试改善查找时间,还是想以某种方式遍历查找时间?
马特·柯蒂斯

您可以用值切换键。
Pawel Zubrycki 2011年

Answers:


65

即使已经发布了正确的答案,我还是想添加一个演示如何干净地进行此演示:

template<typename A, typename B>
std::pair<B,A> flip_pair(const std::pair<A,B> &p)
{
    return std::pair<B,A>(p.second, p.first);
}

template<typename A, typename B>
std::multimap<B,A> flip_map(const std::map<A,B> &src)
{
    std::multimap<B,A> dst;
    std::transform(src.begin(), src.end(), std::inserter(dst, dst.begin()), 
                   flip_pair<A,B>);
    return dst;
}

int main(void)
{
    std::map<int, double> src;

    ...    

    std::multimap<double, int> dst = flip_map(src);
    // dst is now sorted by what used to be the value in src!
}

通用关联源(需要C ++ 11)

如果您std::map对源关联容器(例如std::unordered_map)使用替代方法,则可以编写单独的重载,但最终操作仍然相同,因此,使用可变参数模板的广义关联容器可用于任一映射构造:

// flips an associative container of A,B pairs to B,A pairs
template<typename A, typename B, template<class,class,class...> class M, class... Args>
std::multimap<B,A> flip_map(const M<A,B,Args...> &src)
{
    std::multimap<B,A> dst;
    std::transform(src.begin(), src.end(),
                   std::inserter(dst, dst.begin()),
                   flip_pair<A,B>);
    return dst;
}

这两个工作std::map,并std::unordered_map 与倒装的来源。


1
感谢您的示例代码。它帮助我概括了解决方案。现在,我可以轻松拍摄任何地图。
2011年

15
+1为干净的解决方案。但是,如果目标地图是多地图,则最好避免与相同“值”键发生冲突。
theosem

4
如果多个值具有相同的值,则此解决方案将不起作用,因为反向映射不能有两个具有相同键的对。对于原始地图中的独特价值,此解决方案有效!
Jim Huang

如theosem所建议,对其进行了编辑,以使结果为多图。
ecotax

添加了附录,以包括对通用关联容器的支持,而不仅仅是对std::map。好的回答,顺便说一句,奥利弗。
WhozCraig 2015年

43

我需要类似的东西,但是翻转后的地图对我来说不起作用。我只是将地图(下面的频率)复制到成对的向量中,然后根据需要对成对进行排序。

std::vector<std::pair<int, int>> pairs;
for (auto itr = freq.begin(); itr != freq.end(); ++itr)
    pairs.push_back(*itr);

sort(pairs.begin(), pairs.end(), [=](std::pair<int, int>& a, std::pair<int, int>& b)
{
    return a.second < b.second;
}
);

3
[=]部分的意思是什么
Tanner Summers

@TannerSummers这是lambda表达式的一部分。 en.cppreference.com/w/cpp/language/lambda
NielW '16

这是一个很好的答案。通过传入带有所需分类逻辑的适当operator()重载的Function对象,可以使分类函数更加模块化。很好地使用了lambda逻辑。
h0r53

我正在使用此代码,它可以在gcc 4.9.x中很好地编译,但不能在4.8.x上编译-但是,const在lambda的两个参数前面进行指定可以修复该问题。这可能是gcc 4.8.x的错误,还是让gcc 4.9.x超出了标准要求?
2013年

@Keiji好问题。Lambda支持已在C ++ 0x(现在为11)中添加。如果添加C ++ 11标志,它应该可以工作。stackoverflow.com/questions/16886591/how-do-i-enable-c11-in-gcc
NielW

15

如果要按排序顺序在地图中显示值,则将地图中的值复制到vector并对向量进行排序。


1
+1:这(或其变体,例如创建“逆”图)是正确的答案。
奥利弗·查尔斯沃思

13
如果我这样做。然后我松开键和值之间的关系。例如,我有map <long,double> id2Score。它包含所有ID(其中ID不为0..n,可能类似于1、5、13等)和得分。然后,如果我创建一个带有得分并排序的向量,那么我将丢失与哪个得分相关的ID信息。
user619237 2011年

// @ user619237:不是真的。您可以对向量进行排序。获取最大值或最小值或所需的任何值。然后遍历原始地图,并在地图的值中查找与该值的匹配(“
it-

8

我喜欢奥利(Oli)的答案(翻转地图),但似乎有一个问题:容器地图不允许两个具有相同键的元素。

一种解决方案是使dst类型为multimap。另一个是将src转储到向量中并对向量进行排序。前者需要对Oli的答案进行少量修改,而后者可以使用STL复制来实现

#include <iostream>
#include <utility>
#include <map>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
  map<int, int> m;
  m[11] = 1;
  m[22] = 2;
  m[33] = 3;

  vector<pair<int, int> > v;
  copy(m.begin(),
       m.end(),
       back_inserter<vector<pair<int, int> > >(v));

  for (size_t i = 0; i < v.size(); ++i) {
    cout << v[i].first << " , " << v[i].second << "\n";
  }

  return 0;
};

5

要在使用多图的Oli解决方案(https://stackoverflow.com/a/5056797/2472351)上构建,可以将他使用的两个模板函数替换为以下内容:

template <typename A, typename B>
multimap<B, A> flip_map(map<A,B> & src) {

    multimap<B,A> dst;

    for(map<A, B>::const_iterator it = src.begin(); it != src.end(); ++it)
        dst.insert(pair<B, A>(it -> second, it -> first));

    return dst;
}

这是一个示例程序,显示了执行翻转后保留的所有键值对。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>

using namespace std;

template <typename A, typename B>
multimap<B, A> flip_map(map<A,B> & src) {

    multimap<B,A> dst;

    for(typename map<A, B>::const_iterator it = src.begin(); it != src.end(); ++it)
        dst.insert(pair<B, A>(it -> second, it -> first));

    return dst;
}

int main() {

    map<string, int> test;
    test["word"] = 1;
    test["spark"] = 15;
    test["the"] = 2;
    test["mail"] = 3;
    test["info"] = 3;
    test["sandwich"] = 15;

    cout << "Contents of original map:\n" << endl;
    for(map<string, int>::const_iterator it = test.begin(); it != test.end(); ++it)
        cout << it -> first << " " << it -> second << endl; 

    multimap<int, string> reverseTest = flip_map(test);

    cout << "\nContents of flipped map in descending order:\n" << endl;
    for(multimap<int, string>::const_reverse_iterator it = reverseTest.rbegin(); it != reverseTest.rend(); ++it)
        cout << it -> first << " " << it -> second << endl; 

    cout << endl;
}

结果:

在此处输入图片说明


便携式解决方案-还可与旧版vs一起使用。代码中的指纹也较小。
TarmoPikaro

4

您无法通过std::map这种方式进行排序,因为地图中的条目是按键排序的。如果要按值排序,则需要创建一个std::map交换了键和值的新值。

map<long, double> testMap;
map<double, long> testMap2;

// Insert values from testMap to testMap2
// The values in testMap2 are sorted by the double value

请记住,双键在testMap2使用中必须唯一std::multimap


谢谢。但是我知道这个解决方案。除此之外,还有其他解决方案吗?
2011年

3

一个std::map排序由它的价值在本质上是一个std::set。到目前为止,最简单的方法是将地图中的所有条目复制到一个集合中(从此处获取并改编)

template <typename M, typename S> 
void MapToSet( const  M & m, S & s )
{
    typename M::const_iterator end = m.end();
    for( typename M::const_iterator it = m.begin(); it != end ; ++it )
    {
        s.insert( it->second );
    }
}

一个警告:如果地图包含具有相同值的不同键,则它们将不会插入到集合中并丢失。


3

另一个解决方案是使用std :: make_move_iterator来构建新的向量(C ++ 11)

    int main(){

      std::map<std::string, int> map;
       //Populate map

      std::vector<std::pair<std::string, int>> v {std::make_move_iterator(begin(map)),
                                      std::make_move_iterator(end(map))};
       // Create a vector with the map parameters

       sort(begin(v), end(v),
             [](auto p1, auto p2){return p1.second > p2.second;});
       // Using sort + lambda function to return an ordered vector 
       // in respect to the int value that is now the 2nd parameter 
       // of our newly created vector v
  }

2

翻转的结构可能不再是一个映射,而是一个多重映射,因此在上面的flip_map示例中,并非B中的所有元素都必定会出现在结果数据结构中。


1

您可以考虑使用boost :: bimap,它可能会给您一种感觉,即地图同时按键和按值排序(不过,这实际上并没有发生)


1

在下面的示例代码中,我编写了一种简单的方法来在word_map映射中输出高位单词,其中key是字符串(word),而值是unsigned int(出现单词)。

这个想法很简单,找到当前的最高单词并将其从地图中删除。它没有经过优化,但是在地图不大时效果很好,我们只需要输出前N个字,而不是对整个地图进行排序。

const int NUMBER_OF_TOP_WORDS = 300;
for (int i = 1; i <= NUMBER_OF_TOP_WORDS; i++) {
  if (word_map.empty())
    break;
  // Go through the map and find the max item.
  int max_value = 0;
  string max_word = "";
  for (const auto& kv : word_map) {
    if (kv.second > max_value) {
      max_value = kv.second;
      max_word = kv.first;
    }
  }
  // Erase this entry and print.
  word_map.erase(max_word);
  cout << "Top:" << i << " Count:" << max_value << " Word:<" << max_word << ">" <<     endl;
}

0

在这种情况下,我们应该将地图转换为多图。我认为将映射转换为set不好,因为如果原始映射中有很多重复值,我们将丢失很多信息。这是我的解决方案,我定义了按值排序的小于比较器(cmp函数)。我们可以根据需要自定义cmp函数。

std::map<int, double> testMap = { {1,9.1}, {2, 8.0}, {3, 7.0}, {4,10.5} };
auto cmp = [](const double &lhs,
              const double &rhs)->bool
{
    return lhs < rhs;
};
std::multimap<double, int, decltype(cmp)> mmap(cmp);
for (auto item : testMap)
    mmap.insert(make_pair(item.second, item.first));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.