有一组自定义结构元素:
struct MyStruct
{
int id;
std::string currencyCode;
int month;
int year;
int amount;
};
此数据将显示在某个表中,该表允许按多列排序(通过单击按住Ctrl键的表列)。
按一个属性对客户对象集合进行排序非常简单:
vector<MyStruct> values;
std::sort( values.begin( ), values.end( ), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
return lhs.key < rhs.key;
});
或
struct MyStruct_x_Greater
{
bool operator()( const MyStruct& lMyStruct, const MyStruct& rMyStruct ) const {
return lMyStruct.x < rMyStruct.x;
}
};
std::sort( values.begin(), values.end(), MyStruct_x_Greater() );
但是如何通过几个属性进行排序(类似于sql ORDER BY column1 DESC, column2 ASC
)?
答案 0 :(得分:34)
只需更换比较器即可。假设您想按年份,然后按月,然后按金额订购,那么:
std::sort( values.begin(), values.end(), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
return std::tie(lhs.year, lhs.month, lhs.amount) <
std::tie(rhs.year, rhs.month, rhs.amount);
});
std::tuple
的{{1}}进行字典比较,因此不需要自己动手(并且冒着错误的风险)。要对属性执行降序排序,只需为该属性交换operator<
和lhs
。
答案 1 :(得分:14)
您需要选择 在运行时 的排序顺序,这与大多数类似的问题(以及您收到的一些下意识的答案)不同。我建议你给每个字段一个id号,并添加一个函数来比较id:
指定的字段template <typename T>
int cmp(const T& lhs, const T& rhs)
{
return lhs < rhs ? -1 : lhs == rhs ? 0 : 1;
}
struct MyStruct
{
int id; // 0
std::string currencyCode; // 1
int month; // 2
int year; // 3
int amount; // 4
int cmp(int field_id, const MyStruct& rhs) const
{
switch (field_id)
{
case 0: return cmp(id, rhs.id);
case 1: return cmp(currencyCode, rhs.currencyCode);
case 2: return cmp(month, rhs.month);
case 3: return cmp(year, rhs.year);
case 4: return cmp(amount, rhs.amount);
default: throw ...cases out of sync with code...;
}
}
};
// update this as your column heading are clicked...
std::vector<int> sort_field_ids = ...;
std::sort(begin(values), end(values),
[&](const MyStruct& lhs, const MyStruct& rhs)
{
for (auto fid : sort_field_ids)
{
int cmp = lhs.cmp(fid, rhs);
if (cmp) return cmp == -1;
}
// fall back on something stable and unique
return lhs.id < rhs.id;
});
要支持降序排序,请维护一个额外的标记(例如std::vector<std::pair<int, bool>> sort_field_ids
),然后return (cmp == -1) ^ descending;
(从fid,descending
中提取pair
,或将其称为{{ 1}} / .first
如果您愿意的话。)
更好的是,找一个图形库,它有一个像样的网格/表格小部件,可以在内部进行排序。
答案 2 :(得分:9)
通过为字段分配ID来解决这个问题有几个缺点:
throw
所示)或者,您可以定义&#34;比较器&#34;,返回-1
,0
或1
,具体取决于第一个参数是否小于,等于或分别大于第二个。然后可以非常一般地组合和反转这些比较器,并将其用作排序谓词。
给出结构字段的一组这些比较器,你可以这样编写它们:
Comparator byYearReverseAndMonth = compose(reverse(byYear), byMonth);
std::sort(values.begin(), values.end(), with(byYearReverseAndMonth));
编辑以回复评论:
当然,在编译时定义比较器和反向比较器的每个组合是不。相反,可以在运行时收集所需的比较器以进行下一次排序,例如,在
vector<Comparator>
中。例如,比较器实例可以与表的列相关联:vector<Comparator> comparators; for (each selected column) { comparators.push_pack(comparatorFor(column)); }
然后可以将这些比较器组合成一个比较器,并在所有比较器上进行简单的循环:
Comparator composedComparator = comparators[0]; for (int i=1; i<comparators.size(); ++i) { composedComparator = compose(comparator, comparators[i]); } sort(v.begin(),v.end(),with(composedComparator));
这可能是什么样子的草图:
#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
struct MyStruct
{
int id;
std::string currencyCode;
int month;
int year;
int amount;
};
typedef std::function<int(const MyStruct& s0, const MyStruct& s1)> Comparator;
typedef std::function<bool(const MyStruct& s0, const MyStruct& s1)> Predicate;
template <typename T>
std::function<int(const T&, const T&)> compose(
std::function<int(const T&, const T&)> c0,
std::function<int(const T&, const T&)> c1)
{
return[c0, c1](const T& t0, const T& t1) -> int
{
int r0 = c0(t0, t1);
if (r0 != 0)
{
return r0;
}
return c1(t0, t1);
};
}
template <typename T>
std::function<int(const T&, const T&)> reverse(
std::function<int(const T&, const T&)> c)
{
return[c](const T& t0, const T& t1) -> int
{
return -c(t0, t1);
};
}
template <typename T>
std::function<bool(const T&, const T&)> with(
std::function<int(const T&, const T&)> comparator)
{
return[comparator](const T& t0, const T& t1)
{
return comparator(t0, t1) < 0;
};
}
void print(std::vector<MyStruct>& values)
{
for (auto it = values.begin(); it != values.end(); ++it)
{
std::cout << (*it).month << "-"
<< (*it).year << " id "
<< (*it).id << std::endl;
}
}
int main(int argc, char** argv)
{
std::vector<MyStruct> values;
MyStruct m;
m.year = 1981; m.month = 1; m.id = 4; values.push_back(m);
m.year = 1980; m.month = 2; m.id = 5; values.push_back(m);
m.year = 1980; m.month = 4; m.id = 2; values.push_back(m);
m.year = 1980; m.month = 3; m.id = 3; values.push_back(m);
m.year = 1980; m.month = 4; m.id = 1; values.push_back(m);
std::cout << "Before sorting" << std::endl;
print(values);
Comparator byMonth = [](const MyStruct& s0, const MyStruct& s1)
{
if (s0.month < s1.month) return -1;
if (s0.month > s1.month) return 1;
return 0;
};
Comparator byYear = [](const MyStruct& s0, const MyStruct& s1)
{
if (s0.year < s1.year) return -1;
if (s0.year > s1.year) return 1;
return 0;
};
Comparator byId = [](const MyStruct& s0, const MyStruct& s1)
{
if (s0.id < s1.id) return -1;
if (s0.id > s1.id) return 1;
return 0;
};
Comparator byYearAndMonth = compose(byYear, byMonth);
std::sort(values.begin(), values.end(), with(byYearAndMonth));
std::cout << "After sorting by year and month:" << std::endl;
print(values);
Comparator byYearReverseAndMonth = compose(reverse(byYear), byMonth);
std::sort(values.begin(), values.end(), with(byYearReverseAndMonth));
std::cout << "After sorting by year reverse and month:" << std::endl;
print(values);
Comparator byYearAndMonthAndId = compose(byYearAndMonth, byId);
std::sort(values.begin(), values.end(), with(byYearAndMonthAndId));
std::cout << "After sorting by year and month and id:" << std::endl;
print(values);
return 0;
}
(对于潜在的失礼道歉,我是C ++新手)
答案 3 :(得分:5)
仅当前键的值相等时才使用下一个键:
rhs
在此示例中,条目的订购时间为[ ]( const MyStruct& lhs, const MyStruct& rhs )
{
if(lhs.key1 != rhs.key1)
return lhs.key1 < rhs.key1;
if(lhs.key2 != rhs.key2)
return lhs.key2 < rhs.key2;
...
return lhs.keyN < rhs.keyN;
}
,而不是key1
,依此类推,直至key2
。
答案 4 :(得分:4)
按几个属性排序意味着定义重要性顺序。例如,按年份,月份:年份排序比月份更重要:
vector<MyStruct> values;
std::sort( values.begin( ), values.end( ), [ ]( const MyStruct& lhs, const MyStruct& rhs )
{
return lhs.year < rhs.year || ( lhs.year == rhs.year && lhs.month < rhs.month );
});
答案 5 :(得分:3)
完成你想要的东西有点麻烦的方法是分几步完成(这也适用于C ++ 03)
struct ColumnOneDESC{...};
struct ColumnTwoASC{...};
...
std::stable_sort( values.begin(), values.end(), ColumnTwoASC());
std::stable_sort( values.begin(), values.end(), ColumnOneDESC());
...
对于cource,你可以使用std :: not或者这样的
来使它更通用