繁体   English   中英

如何按给定字段对结构向量进行排序?

[英]How to sort vector of structs by given field?

我需要按给定字段对结构向量进行排序。

对于面临相同问题的任何人:我刚刚对 function 进行了排序,您可以在其中传递 lambda 任意数量的变量(不仅仅是其中的两个)。


这里我使用了 lambda 表达式,但我无法定义要排序的字段,因为 lambda 只接受传递的变量。

sort(patients.begin(), patients.end(), [](Patient a, Patient b) { return a.name > b.name; });

我不能这样做:

string sortfield = name;
    sort(patients.begin(), patients.end(), [](Patient a, Patient b) 
{ 
   if(sortfield == "name") return a.name < b.name;
   else if(sortfield == "age") return a.age < b.age;
...
});
  1. 因为它会导致错误,因为 lambda 只能使用传递的变量,不能使用本地变量。
  2. 将很难扩展。 每次我向结构中添加新字段时,我都必须返回 go 并再添加一个“else if”

我还尝试使用指向函数成员的指针,但它的工作方式相同。

我可以像这样使用 switch-case:

enum Fields
{
    Name, Gender, DateOfBirth
};
Fields field;
switch (field)
{
case Fields::Name:
    sort(patients.begin(), patients.end(), [](Patient a, Patient b) { return a.name > b.name; });
    break;

case Fields::Gender:
    sort(patients.begin(), patients.end(), [](Patient a, Patient b) { return a.gender > b.gender; });
    break;

case Fields::DateOfBirth:
    sort(patients.begin(), patients.end(), [](Patient a, Patient b) { return a.dateOfBirth > b.dateOfBirth; });
    break;

default:
    break;
}

但它会打破OCP(开闭原则)。 当结构被扩展时,我将不得不多次回来添加更多的“案例”块。

有一个更好的方法吗?

您可以使用另一个 lambda 来选择要比较的字段:

template <typename F>
void my_sort(auto& patients, F f) {
    sort(patients.begin(), patients.end(), [f](const Patient& a,const Patient& b) { return f(a) > f(b); });
}


my_sort(patients,[](const Patient& a) { return a.name; });

因为它会导致错误,因为 lambda 只能使用传递的变量,不能使用本地变量。

这是错误的。 您当然可以在 lambda 中使用局部变量。 捕获是方括号的作用。

string sortfield = name;
sort(patients.begin(), patients.end(), [&sortfield](Patient a, Patient b) 
{
    if(sortfield == "name") return a.name < b.name;
    else if(sortfield == "age") return a.age < b.age;
    // ...
});

另一方面,如果修改了Patient class,这种方法确实会增加维护负担。 如果将切换逻辑放在 class 中,则可以将更改本地化到 class 实现。(使用字符串还是枚举由您决定,但请记住,编译器不会捕获字符串中的拼写错误。)

class Patient {
  public:
    enum Fields { Name, Gender, DateOfBirth };
    // For readability:
    using CompareFunction = std::function<bool(const Patient&, const Patient&)>;

    // This is a function that returns a functional. If you have not seen the
    // concept before, it might take some time to grasp the idea.
    static CompareFunction SortBy(Fields);

    // And the rest of the class definition
};

// Implementation:
Patient::CompareFunction Patient::SortBy(Fields field)
{
    switch (field)
    {
        // We are returning a lambda, which the sort function will invoke.
        case Name:        return [](const Patient &a, const Patient &b) { return a.name > b.name; };
        case Gender:      return [](const Patient &a, const Patient &b) { return a.gender > b.gender; };
        case DateOfBirth: return [](const Patient &a, const Patient &b) { return a.dateOfBirth > b.dateOfBirth; };
    }
    // ERROR! If we missed an enumerate, we should have the compiler complain.
    // Figure out what you want to do in this case.    
}

如果你想添加一个新的排序字段,你仍然需要更新它(但如果你添加一个没有排序选项的新成员则不需要)。 但是,此代码是通过 class 实现的,因此至少您不必遍历代码库来查找要更新的地方。

要在执行排序的代码中使用它:

Patient::Fields field = Patient::Name; // as an example
sort(patients.begin(), patients.end(), Patient::SortBy(field));

作为一种特殊情况,如果所有潜在的排序字段都具有相同的类型(不太可能用于患者,但可能用于其他情况)和公共的(在许多情况下不推荐),则可以使用带有指向成员的指针的 lambda而不是SortBy方法。 这种方式的好处是修改class时不需要专门维护。 限制是由于限制(相同类型和公共),它通常不是一个选项。

std::string Patient::* field = &Patient::name;
sort(patients.begin(), patients.end(), [field](const Patient &a, const Patient &b) {
    return a.*field < b.*field;
});

我想如果您使用指向成员函数的指针,则可以绕过“公共”限制。 相同类型的限制可能仍然是一个障碍,而且语法更混乱。

auto field = &Patient::get_name; // assuming get_name() is the getter.
sort(patients.begin(), patients.end(), [field](const Patient &a, const Patient &b) {
    return (a.*field)() < (b.*field)();
});

在这一点上,另一个答案中的“另一个 lambda”方法可能更可取(基于样式)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM