diff options
| author | Tom Smeding <tom@tomsmeding.com> | 2026-09-04 00:42:14 +0200 |
|---|---|---|
| committer | Tom Smeding <tom@tomsmeding.com> | 2026-09-04 00:42:14 +0200 |
| commit | 434fe2961bdd7d9a7fb5b28c6d0d3f969d4149f2 (patch) | |
| tree | 30f74eade2057c1c51bd649d03e8bc8dd2bb4b47 | |
| parent | f6554e35926287a43198c594e1e097219e5e68c6 (diff) | |
| -rw-r--r-- | tabulate.cpp | 56 |
1 files changed, 55 insertions, 1 deletions
diff --git a/tabulate.cpp b/tabulate.cpp index 7b262da..3099636 100644 --- a/tabulate.cpp +++ b/tabulate.cpp @@ -4,12 +4,14 @@ #include <string_view> #include <unordered_set> #include <unordered_map> +#include <algorithm> #include <map> #include <tuple> #include <memory> #include <cstdlib> #include <cstring> #include <cerrno> +#include <cassert> static void usage(const char *argv0) { @@ -30,7 +32,9 @@ static void usage(const char *argv0) { "\n" "Options:\n" " -h Show help.\n" - " -s DELIM The character that separates fields. Default space (' ').\n"; + " -s DELIM The character that separates fields. Default space (' ').\n" + " -S Sort table, row and column labels lexicographically." + " -n Sort table, row and column labels numerically."; } struct Range { @@ -141,8 +145,29 @@ std::ostream& operator<<(std::ostream &os, AlignBase<T, Dir::right> al) { return os << Spaces(al.outw - al.sw) << al.s; } +enum class Sort { + no, lex, num +}; + +bool sort_lessthan(Sort type, const std::string &a, const std::string &b) { + switch (type) { + case Sort::no: assert(false); + case Sort::num: { + int an = 0, bn = 0; + try { an = std::stoi(a); } catch (...) {} + try { bn = std::stoi(b); } catch (...) {} + if (an != bn) return an < bn; + } + // fallthrough + case Sort::lex: + return a < b; + } + assert(false); +} + int main(int argc, char **argv) { char sepchar = ' '; + Sort opt_sort = Sort::no; std::vector<Fieldspec> fieldspecs; fieldspecs.reserve(4); @@ -167,6 +192,8 @@ int main(int argc, char **argv) { return 1; } break; + case 'S': opt_sort = Sort::lex; break; + case 'n': opt_sort = Sort::num; break; default: std::cerr << "Invalid option '-" << argv[i][j] << "'" << std::endl; return 1; @@ -256,6 +283,33 @@ int main(int argc, char **argv) { } } + if (opt_sort != Sort::no) { + std::vector<int> renumber[3]; + for (int key = 0; key < 3; key++) { + const size_t N = labels[key].size(); + renumber[key].resize(N); + for (size_t i = 0; i < N; i++) renumber[key][i] = i; + std::sort(renumber[key].begin(), renumber[key].end(), [&](int a, int b) { + return sort_lessthan(opt_sort, *labels[key][a], *labels[key][b]); + }); + + std::vector<std::unique_ptr<std::string>> newlabels; + newlabels.reserve(N); + for (size_t i = 0; i < N; i++) + newlabels.push_back(std::move(labels[key][renumber[key][i]])); + labels[key] = std::move(newlabels); + } + + std::map<std::tuple<int, int, int>, std::string> newvalues; + for (auto &pair : values) { + auto [i0, i1, i2] = pair.first; + std::tuple<int, int, int> k = std::make_tuple( + renumber[0][i0], renumber[1][i1], renumber[2][i2]); + newvalues.emplace(k, std::move(pair.second)); + } + values = std::move(newvalues); + } + int leftwid = 0; // does not include table name for (const std::unique_ptr<std::string> &rowname : labels[ROW]) leftwid = std::max<int>(leftwid, swidth(*rowname)); |
