如何按任意列对数据进行动态排序

How to dynamically sort data by arbitrary column(s)

本文关键字:动态 排序 数据 何按任      更新时间:2023-10-16

是否有一种结构组合的数据结构(STL、Boost等)允许我像在这里的表中那样对内存中的数据进行排序http://en.wikipedia.org/wiki/List_of_cities_proper_by_population

我想要一个通用的解决方案,可以处理任意数量和列名称。本质上相似使用sql的order by clause

好吧,你必须做一些腿部工作。

构造一个结构或元组以容纳一行的编译器:

struct row{
   unsigned rank;
   std::string city;
   double area;
   //so on
}; 

在用行填充向量或其他包含时。

std::vector<row> rows;

要排序,请使用std::sort和自定义比较函数,您可以查看某些值。

std::sort(rows.begin(), rows.end(), [](const row& r1, const row& r2)->bool{
    return r1.area < r2.area;
}); //sort by area

这可以通过有一个向量的向量而变得通用,并且比较函数可以从它的环境中捕获变量:参见我的另一个答案

有Boost.Multi-index。不过,您需要提前指定要为其构建索引的所有列。

如果您在SQL中order by一个没有索引的列,它只会进行线性扫描并动态构建一个排序的结果集——如果您想按某个没有预先索引的列排序,那么在C++中也可以这样做。

OK,因此基于以下假设:

  • 要按名称选择排序列吗
  • 您想为每一行存储类似struct的内容(使用变体类型在这里购买的数量有限,但您可以使用元组/类型列表路线)

您需要:

  • 将列名称与实际列匹配的方法
  • 挂起特定类型排序代码的位置

假定输入&输出类型(分别是未排序的容器和排序的容器)是相同的,您可以使用运行时多态性和模板的组合来实现这一点。

这是一个快速草图:

#include <vector>
#include <set>
#include <map>
#include <memory>
#include <iterator>
#include <algorithm>
template <typename Row> class Table
{
    std::vector<Row*> rows;
    class AbstractSorter
    {
    protected:
        // this doesn't have to go in AbstractSorter,
        // but it's only used from the derived classes
        template <typename Comp> static
        std::vector<Row*> sort(std::vector<Row*> const &in, Comp comp)
        {
            std::vector<Row*> out;
            out.reserve(in.size());
            // or copy and sort in place, for example
            std::multiset<Row*, Comp> sorted(comp);
            std::copy(in.begin(), in.end(), std::inserter(sorted, sorted.end()));
            std::copy(sorted.begin(), sorted.end(), std::back_inserter(out));
            return out;
        }
    public:
        virtual ~AbstractSorter() {}
        virtual std::vector<Row*> sort(std::vector<Row*> const &) const = 0;
    };
    typedef std::unique_ptr<AbstractSorter> SortPtr;
    typedef std::map<std::string, SortPtr> SortMap;
    static SortMap sorters;
    template <typename ColType>
    class ConcreteSorter: public AbstractSorter
    {
        ColType Row::*col;
    public:
        ConcreteSorter(ColType Row::*member) : col(member) {}
        virtual std::vector<Row*> sort(std::vector<Row*> const &in) const
        {
            // assuming you have C++11 lambdas, otherwise you'll need to
            // write a comparator too
            return AbstractSorter::sort(
                in,
                [&col](Row *a, Row *b){ return (a->*col) < (b->*col); }
                );
        }
    };
public:
    template <typename ColType>
    static void bindSortableColumn(char const *name, ColType Row::*member)
    {
        sorters.insert(typename SortMap::value_type(
                std::string(name), 
                SortPtr(new ConcreteSorter<ColType>(member))
                ));
    }
    // error handling left as an exercise for the reader
    std::vector<Row*> sortBy(std::string const &name) const
    {
        return sorters[name]->sort(rows);
    }
};
#define SORTABLE_COLUMN(ROW, COL) 
    Table<ROW>::bindSortableColumn(#COL, &ROW::COL);
template <typename Row> typename Table<Row>::SortMap Table<Row>::sorters;
// now to define your own row type    
struct MyRow
{
    int id;
    std::string name;
    double salary;
};
// and the tedious bit: setting up the sorter objects for your columns
// (you could automate this further by using a tuple instead of a regular
//  struct for MyRow)
void init_columns()
{
    SORTABLE_COLUMN(MyRow, id);
    SORTABLE_COLUMN(MyRow, name);
    SORTABLE_COLUMN(MyRow, salary);
}

我会制作一个结构向量,每个结构为该表的1"行"建模。您可以使用std::sort和sort functor按不同的成员对其进行排序,sort funtor只比较您想要排序的成员。

我想我会单独发布一个通用答案

考虑:

typedef std::vector<std::string> row;
std::vector<row > table;

填充每个内部向量,就像它是一行一样,只需确保它们都有相同数量的元素。

然后制作一个可以对指定行进行操作的比较函数

bool compare_index(std::size_t i, const row& v1, const row& v2)
{
    return v1.at(i) < v2.at(i);
}

现在你可以像一样排序

std::size_t column=2; //or which ever column
std::sort(table.begin(), table.end(), 
          std::bind(&compare_index, col, 
              std::placeholders::_1, 
              std::placeholders::_2));