【问题标题】:Vector that contains keys to sort a map by values包含按值对地图进行排序的键的向量
【发布时间】:2018-01-08 15:24:35
【问题描述】:

我有一个:

std::map<long, std::wstring> fNames;       // ex: fNames[FileReferenceNumber] = L"hello.txt"

由于std::map 有排序的键,但没有值(哈希表unordered_map 甚至没有排序),我想创建一个向量:

std::vector<long> v;

将包含键允许fNames 的迭代按值排序

例子:如果我们有

9187 => "hello.txt"
13 => “z.txt”
第1777章

那么v 将是:[1777, 9187, 13],允许迭代 fNames 按值排序:

for (int i = 0; i < v.size(); i++) 
    wcout << fNames[v[i]];     // a.txt, hello.txt, z.txt

有没有办法使用std::sort 创建这个向量v?我不明白怎么做。我应该使用自定义谓词吗?


PS:更好的是:是否可以生成一个已排序的std::vector&lt;wstring&gt; w?即每个元素w[0]w[1] 等都将包含指向fNames 的 wstrings 的“链接”(指针?),但不包含副本(以避免复制字符串数据)?

或者可能是std::vector&lt;wchar_t *&gt; w

【问题讨论】:

  • 第二个multimap&lt;wstring, long&gt;,它将存储排序后的值及其键。在每次更改原始 map&lt;long, wstring&gt; 时创建和排序 vector&lt;wstring&gt; 值将花费太多时间。
  • @ikleschenkov 是否类似于拥有地图 + 它的“反向地图”?如果多个键具有相同的值会发生什么?假设a[1] = "blah"; a[2] = "blah"; 反向映射reversea["blah"] 是什么?如问题中所述,是否可能只有一个用于排序的向量?
  • multimap 将处理重复的“键”(这是来自第一个映射的值)。解决方案取决于您需要多久刷新一次排序值的向量。如果你很少需要这个,那么排序 vector 就可以了。如果你经常需要这个,那么最好“反转”multimap,因为它会比每次更新和排序向量更快。

标签: c++ sorting dictionary vector


【解决方案1】:

使用指向地图元素的指针向量,并对这些指针进行排序。这使您无需复制即可访问 long 和 wstring。

using value_type = std::map<long, std::wstring>::value_type;
std::vector<value_type*> v;
for (auto& e : fNames)
    v.push_back(&e);
auto compare_by_second = [](value_type* lhs, value_type* rhs) {
    return lhs->second < rhs->second;
};
std::sort(v.begin(), v.end(), compare_by_second);

现在您可以通过v[N]-&gt;second 访问wstring 值,通过v[N]-&gt;first 访问长值。

C++11 之前的版本

bool compare_by_second(std::pair<const long, std::wstring>* lhs,
                       std::pair<const long, std::wstring>* rhs)
{
    return lhs->second < rhs->second;
}

// in some function
std::map<long, std::wstring> fNames;
...
std::vector<std::pair<const long, std::wstring>*> v;
for (std::map<long, std::wstring>::iterator b = fNames.begin();
     b != fNames.end(); ++b)
{
    v.push_back(&*b);
}
std::sort(v.begin(), v.end(), compare_by_second);

如果您只需要字符串,而不需要 long,那么您可以只使用指向字符串的指针向量。

bool deref_compare(std::wstring* lhs, std::wstring* rhs)
{
    return *lhs < *rhs;
}

// in some function
std::map<long, std::wstring> fNames;
...
std::vector<std::wstring*> v;
for (std::map<long, std::wstring>::iterator b = fNames.begin();
     b != fNames.end(); ++b)
{
    v.push_back(&b->second);
}
std::sort(v.begin(), v.end(), deref_compare);

【讨论】:

  • @Basj:是std::pair&lt;const long, std::wstring&gt;auto&amp; 将是对该类型的引用。
  • @Basj:添加了 Pre c++11 版本。
  • 非常感谢。据我了解,我们将地图复制到一对向量中,然后对它进行排序。有没有办法只用一个 long 向量来做到这一点? (而不是一对)
  • @Basj:是的。只需将push_back 行更改为v.push_back(b-&gt;first)。在这种情况下你还想按配对字符串的值排序吗?
  • 如果是这样,那么我建议您首先制作一个指向对的指针向量并对其进行排序,就像我在回答中显示的那样。然后制作第二个长向量,并从第一个向量中的对中复制长。
【解决方案2】:

PS:更好的是:是否有可能制作一个 std::vector&lt;wstring&gt; w 排序?即每个元素w[0]w[1] 等。 将包含指向fNameswstrings 的“链接”(指针?),但不包含副本 (避免重复字符串数据)?

vector&lt;wstring&gt; 将包含重复的字符串(因为 CoW - 写入时复制 - 自 C++11 以来,std::[w]string 已被禁止)。如果你想使用const wchar_t* 来避免字符串重复,你可以这样做:

vector<const wchar_t*> sortedFilenames;

// Reserve room in the vector, since we know how many strings to add
sortedFilenames.reserve(fNames.size());

// Add string pointers from map to vector
for (const auto& e : fNames) {
    // Avoid duplicates using const wchar_t*
    sortedFilenames.push_back(e.second.c_str());
}

// Sort the string vector
sort(begin(sortedFilenames), end(sortedFilenames), 
    [](const auto& p1, const auto& p2) {
        // Use whatever sorting rule you need here...
        return wcscmp(p1, p2) < 0;
    }
); 

编辑根据您的评论,您也可以使用vector&lt;const wstring*&gt;,例如:

vector<const wstring*> sortedFilenames;

// Reserve room in the vector, since we know how many strings to add
sortedFilenames.reserve(fNames.size());

// Add string pointers from map to vector
for (const auto& e : fNames) {
    sortedFilenames.push_back(&(e.second));
}

// Sort the string vector
sort(begin(sortedFilenames), end(sortedFilenames),
    [](const auto& p1, const auto& p2) {
        return (*p1) < (*p2); // or whatever sorting rule...
    }
); 

【讨论】:

  • 谢谢。是否可以这样做并拥有vector&lt;wstring *&gt; sortedFilenamesvector &lt;const wstring *&gt;?所以我仍然可以访问wstring 结构,而不仅仅是wchar_t *(例如,如果我需要在迭代时将它与其他东西连接起来,最好已经有一个wstring)
  • 是的,如果你想存储 observing 指向原始wstrings 的指针,你可以使用vector&lt;const wstring *&gt;;我已经用示例代码更新了我的答案。
  • 要获取地图键值的向量,我可以使用 e.first 吗?
  • 是:first --> 键,second --> 与键关联的值。
【解决方案3】:

我将在 中解决这个问题,因为它比 更简单,并且每个主要的编译器供应商都有足够的支持来编译:

制作一个指向字符串的向量并保留足够的空间(出于效率原因)。

std::vector<std::wstring const*> sorted_strings;
sorted_strings.reserve(fNames.size());

填充指针:

for (auto& entry:fNames)
  sorted_strings.push_back( &entry.second );

对它们进行排序:

std::sort(
  begin(sorted_strings), end(sorted_strings),
  [](auto* lhs, auto* rhs) { return *lhs < *rhs; }
);

对于键向量:

std::vector<int> sorted_keys;
sorted_keys.reserve(fNames.size());
for (auto&& entry:fNames)
  sorted_strings.push_back( entry.first );
std::sort(
  begin(sorted_keys), end(sorted_keys),
  [&](int lhs, int rhs) { return fNames[lhs] < fNames[rhs]; }
);

中,您可以取消.first.second

for (auto&[key, value]:fNames)
  sorted_strings.push_back( &value );

使用结构化绑定。

【讨论】:

  • 非常感谢。关于“键向量”部分,for (auto&amp;&amp; entry:fNames) 的 c++11 前版本是什么?我在想for (map&lt;long, wstring&gt;::iterator b = fNames.begin(); b != fNames.end(); ++b),对吗?还有auto之后的双&amp;会变成什么?
  • @basj auto&amp;&amp;是一个转发参考。 C++03 中没有等价物。在这种情况下,它意味着对正在迭代的任何内容的引用绑定,如果需要,生命周期会延长,但无论如何都不会复制,如果可能是可变的,但如果不是,const。在另一个范围内——因为我做了auto&amp;,因为我们在哪里持久化了一个指向循环内成员的指针,因此我们必须保证我们在哪里迭代非临时数据。并且auto&amp; 不会绑定到临时对象。这只是将代码编写为安全的通用代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多