summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom@tomsmeding.com>2026-09-04 00:42:14 +0200
committerTom Smeding <tom@tomsmeding.com>2026-09-04 00:42:14 +0200
commit434fe2961bdd7d9a7fb5b28c6d0d3f969d4149f2 (patch)
tree30f74eade2057c1c51bd649d03e8bc8dd2bb4b47
parentf6554e35926287a43198c594e1e097219e5e68c6 (diff)
Sorting optionsHEADmaster
-rw-r--r--tabulate.cpp56
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));