![](/img/trans.png)
[英]How to sort a vector of structs based on a vector<string> within the vector to be sorted?
[英]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;
...
});
我还尝试使用指向函数成员的指针,但它的工作方式相同。
我可以像这样使用 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.