【问题标题】:How to sort QMap<QString, myStruct>?如何对 QMap<QString, myStruct> 进行排序?
【发布时间】:2018-03-27 16:28:51
【问题描述】:

我有一个QMap&lt;QString, myStruct&gt;

myStruct {
    QString firstname;
    QString lastname;
    QString status;
}

如何根据优先级顺序对QMap 进行排序:status 然后firstname 然后lastname

【问题讨论】:

  • 也许您应该使用另一个容器,因为QMap 总是按键排序!
  • 实际上这个 QMap 是固定的,我不能再更改了987654329@,然后对这个QList进行排序,然后将排序后的列表再次放入QMap。您如何看待这个解决方案?
  • 那无济于事。当您插入回地图时,它将再次根据密钥“重新排序”。根据您的用例,您可能想要使用两个容器。首先是按键排序的地图。然后是一个带有参考包装器的向量(或类似的)到地图中的结构。您可以按任何方式对矢量进行排序。

标签: c++ qt sorting qsort qmap


【解决方案1】:

据我了解,您希望检索按上述方式排序的地图值,但仍可以访问该键。对吧?

快速地说,地图是由 key 自动排序的 &lt;key, value&gt; 对的集合,那么您可以尝试按 value 手动排序的 &lt;value, key&gt; 对列表反而。类似于QList&lt;QPair&lt;myStruct, QString&gt;&gt;,同时覆盖operator&lt;myStruct

struct myStruct {
    QString firstname;
    QString lastname;
    QString status;

    bool operator<(const myStruct& o) const {
      return std::tie(status, firstname, lastname) <
             std::tie(o.status, o.firstname, o.lastname);
    }
};

QMap<QString, myStatus> map; // your original map
QList<QPair<myStatus, QString>> inv;

// Populate the inverted list
for (auto k : map.keys()) {
  inv.append(QPair<myStatus, QString>(map[k], k));
}

std::sort(std::begin(inv), std::end(inv));

for (auto p : inv) {
  qDebug() << p.first.status << p.first.firstname << p.first.lastname << p.second;
}

当然,它是一次性使用结构,不会与您的原始地图保持更新,但您提到地图是固定的(常量?)所以它可能不是那么问题来了。

顺便说一句,QMap 可用于反向查找,但仅在 myStruct 部分的值也是唯一的情况下(因此它们也可以用作键),否则您可能会覆盖构造逆映射时的值。

注意:std::tie 仅用于简化元组的排序条件(因此您需要包含 &lt;tuple&gt;)。

更新

回答您的一个 cmets:是的,您还可以指定自己的比较谓词,然后避免覆盖 operator&lt;,但我认为它更难阅读且可重用性较低:

std::sort(std::begin(inv), std::end(inv),
  [](const QPair<myStatus, QString>& lhs, const QPair<myStatus, QString>& rhs) {
    return std::tie(lhs.first.status, lhs.first.firstname, lhs.first.lastname) <
           std::tie(rhs.first.status, rhs.first.firstname, rhs.first.lastname);
});

当然,您可以根据需要实现该比较 lambda,我再次使用 std::tie 来简化帖子中的逻辑。不利的一面是,如果您需要在多个地方生成逆映射,则必须在各处重复 lambda 表达式(当然,也可以创建一个函数来创建逆映射)。

作为旁注,如果您好奇,lhsrhs 分别指左侧右侧,在此如果它们被排序算法用作lhs &lt; rhs 来比较元素。

最后,如果您想避免std::tie,则必须手动进行比较(下面的代码修改了第一个版本的operator&lt;):

bool operator<(const myStruct& o) const {
  if (status < o.status) return true;
  if (status > o.status) return false;
  // status == o.status, move to next attribute

  if (firstname < o.firstname) return true;
  if (firstname > o.firstname) return false;
  // firstname== o.firstname, move to next attribute

  if (lastname < o.lastname) return true;
  if (lastname > o.lastname) return false;

  return false; // are equal
}

【讨论】:

  • 非常感谢@cbuchart。你的解决方案真的很棒。但有一件事我不明白。你覆盖了operator&lt;,但我看不到它在哪里使用。你能解释清楚一点吗?
  • 此外,除了std::tie,我们还有其他算法对元素进行排序,这样我们就不必覆盖任何东西了吗?
  • @gnase operator&lt;std::sort 用来对列表进行排序。 std::tie 用于简化比较代码,因此您需要手动考虑所有情况。
  • 非常感谢@cbuchart。通过你的回答,我明白了更多。我接受了。祝你有美好的一天!
【解决方案2】:

您不能手动对QMap 进行排序,您必须为此使用QList(或QVector)并在其上使用std::sort。使用QMap::values() 将映射中的值(结构)提取到列表中,然后实现比较函数/方法并使用std::sort 调用它。请参阅 cbuchart 的答案,了解如何执行此操作的一些提示。

在值更改时保持地图和列表同步是一个不同的问题,如果这是一个要求,您应该创建一个单独的问题,添加 MCVE 以及有关您尝试的更多详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 2011-11-09
    • 2014-04-18
    • 2016-05-17
    相关资源
    最近更新 更多