如何根据其值的属性对地图进行排序?

Posted

技术标签:

【中文标题】如何根据其值的属性对地图进行排序?【英文标题】:How can I sort a map according to a property of its values? 【发布时间】:2014-06-16 10:11:25 【问题描述】:

我创建了一个具有如下矢量的地图:

map<int,vector<int>> mymap;

如何根据map包含的向量的nth值对这个map进行排序?

【问题讨论】:

你不能。 std::map 根据其键排序。 是的,你可以。您应该能够提供一个比较器作为地图声明的第三种类型。请参阅cplusplus.com/reference/map/map 但是获取值可能很棘手,甚至是不可能的。 我同意@juanchopanza,因为比较器只担心插入元素的Key 而不是Value 部分,因此这是不可能的。顺便说一句,我 +1 是的,你可以 意识到这是错误的 :( @Samuel 所以你可以,但它可能是不可能的? “可以”的有趣定义。 你为什么要这个? 【参考方案1】:

你不能。你可以提供一个自定义比较器来使底层数据以不同于默认方式的方式排序,但这仅与 keys 相关,与 价值观。如果您要求容器的元素以某种特定的、值定义的顺序存在,那么您使用了错误的容器。

您可以切换到set,并利用“键”和“值”之间没有区别的事实,自己破解底层排序:

template <std::size_t N>
struct MyComparator

   typedef std::pair<int, std::vector<int>> value_type;
   bool operator()(const value_type& lhs, const value_type& rhs)
   
      return lhs.second.at(N) < rhs.second.at(N);
   
;

/**
 * A set of (int, int2,) pairs, sorted by the 2nd element in
 * the 2nd item of each pair.
 */
std::set<std::pair<int, std::vector<int>>, MyComparator<1>> my_data;

int main()

    my_data.insert(std::make_pair(1, std::vector<int>0,5,0,0));
    my_data.insert(std::make_pair(2, std::vector<int>0,2,0,0));
    my_data.insert(std::make_pair(3, std::vector<int>0,1,0,0));
    my_data.insert(std::make_pair(4, std::vector<int>0,9,0,0));

    for (const auto& el : my_data)
        std::cout << el.first << ' ';


// Output: 3 2 1 4

(live demo)

但是,如果您仍然需要对键以及执行查找,那么您真的遇到了麻烦,需要重新考虑一些事情。您可能需要复制数据或提供索引向量。

【讨论】:

可能是一个愚蠢的问题来清除我的疑问,但是:unordered_mapmap 只是在 C++ 的数据结构实现中泄漏的实现细节,称为 map,它本质上是无序的,是吗对吗? @legends2k:我不确定有多少人会同意我的观点,但是,IMO,是的。我认为unordered_map 这个名字具有误导性。 unsorted_map 可能更有意义。但正如已知的那样,我可能会以错误的方式解决这个问题。 @Erbureth:地图需要根据计算机具有线性增加的内存地址这一事实以及树算法的工作方式进行排序。是的,该标准断言该要求。请仔细阅读完整答案,并注意“排序”和“有序”之间的区别,这很重要。 @Erbureth:地图绝对以线性方式存储,计算机中的每一条数据也是如此。它们没有连续地存储是另一回事。我不完全确定你想表达什么。 @legends2k:元素编号与它有什么关系?您完全忽略了我的观点,即您计算机中的每条数据都是沿着直线地址找到的。因此,您计算机中的每条数据都以某种方式相对于其他数据进行线性排序。我们还没有发明 3D 内存寻址!【参考方案2】:

map&lt;int,vector&lt;int&gt;&gt; mymap;

如何根据map包含的向量的第n个值对这个map进行排序?

这只有在您准备将第 nth 值也用作整数键时才有可能,如一致分配:

mymap[v[n - 1]] = v;

如果您这样做,您可能会考虑使用set&lt;vector&lt;int&gt;&gt;,它会删除该“关键”元素的冗余存储 - 但是您需要提供自定义比较......

如果您设想采用没有该排序的现有填充地图,然后对其元素进行排序 - 这是完全不可能的。您必须将元素复制到另一个容器中,例如在第 nth 元素上排序的 set,或填充后 std::sortvector

【讨论】:

【参考方案3】:

如果我理解正确,您可以通过以下方式(构建)向地图添加元素

std::vector<int> v =  1, 2, 3 ;
std::vector<int>::size_type n = 2;

mymap[v[n]] = v;

这是一个例子

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <cstdlib>
#include <ctime>

int main() 

    std::srand( ( unsigned )time( 0 ) );
    const size_t N = 10;
    std::map<int, std::vector<int>> m;

    for ( size_t i = 0; i < N; i++ )
    
        std::vector<int> v( N );
        std::generate( v.begin(), v.end(), [] return std::rand() % N;  );
        m[v[0]] = v;
    

    for ( auto &p : m )
    
        for ( int x : p.second ) std::cout << x << ' ';
        std::cout << std::endl;
    

    return 0;

输出是

0 1 7 8 1 2 9 0 0 9 
1 6 3 1 3 5 0 3 1 5 
3 8 0 0 0 7 1 2 9 7 
5 9 5 0 7 1 2 0 6 3 
6 4 7 5 4 0 0 4 2 0 
7 9 8 6 5 5 9 9 4 5 
8 3 8 0 5 9 6 6 8 3 
9 5 4 7 4 0 3 5 1 9 

考虑到可能存在重复的向量(即具有相同值的第 n 个元素(在我的示例中 n 等于 0),因此某些向量将不会添加到地图中。如果您想要有重复,那么你应该使用例如std::multimap

您还可以根据现有地图的标准构建新地图。

【讨论】:

我也想过这个,但是如果 OP 的 key 不是微不足道的呢? 那么重复项呢?这不仅仅是您正在执行的排序。 @legends2k 他已经将他的地图描述为 map> mymap; @Lightness Races in Orbit 重复没有任何问题。如果你想有重复,那么你应该使用 std::multimap。 @VladfromMoscow 但我们不知道int 部分目前是什么【参考方案4】:

您可以滥用 c++ 映射使用按其键排序的树这一事实。这意味着您可以创建一个新地图,将您希望对其进行排序的值作为键,但您也可以创建一个 vector 并引用地图中的项目,然后对该向量(或其他解决方法:你可以有一个排序的向量,并使用map 在你的向量上创建一个索引)。确保在重复键的情况下使用multimap

【讨论】:

A map has no predefined underlying sortedness 那就错了 :) 不过,它们没有重叠的顺序。

以上是关于如何根据其值的属性对地图进行排序?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据 Dart 中的 int 属性对所有 Class 属性进行排序?

根据值的属性对 PowerShell 哈希表进行排序

如何根据每行的值范围对对象数组进行排序?

根据Java中的值对地图进行排序的最简单方法是啥?

如何根据特定顺序对休眠结果进行排序

Python入门题045:根据对象属性进行排序