diff --git a/CHANGELOG.md b/CHANGELOG.md index e5317cd..5435af8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ folgen [Semantic Versioning](https://semver.org/lang/de/). Die Version steht in - Tabellen, Indexdateien und `substitute` werden über eine temporäre Datei mit `rename` geschrieben; ein Abbruch lässt die alte Datei statt einer halben neuen. - `index -mb` und `-mi` sortieren nach dem Schlüssel ohne umgebende Leerzeichen, so wie `search` vergleicht. - `sorttable -r` kehrte auch die Reihenfolge gleicher Schlüssel um; sie bleibt jetzt erhalten. +- `jointable -n` wurde stillschweigend ignoriert. Wie in der Man-Page vergleicht es die Schlüssel jetzt als Zahlen (`01`, `1` und ` 1.0` passen zusammen). - Spaltennamen, die `justify` mit Leerzeichen aufgefüllt hat, wurden nicht mehr gefunden: `column Name` lieferte eine leere Spalte, `index` brach ab. Leerraum um die Namen im Kopf zählt jetzt nicht mehr. ## [0.7.0] - 2026-10-12 diff --git a/README.md b/README.md index ccd6859..7057110 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ sind nach dem Handbuch neu geschrieben. mise trust # einmal nach dem Clone: mise.toml freigeben mise install # Ruby 3.4 mise run install # legt die Symlinks row, column, total ... in bin/ an -mise run test # 131 Tests und die Beispiele aus dem Handbuch +mise run test # 132 Tests und die Beispiele aus dem Handbuch ``` Ohne `mise trust` brechen in einem frisch geklonten Ordner alle `mise`-Befehle ab. diff --git a/doc/review.md b/doc/review.md index b0e4672..3ecd55e 100644 --- a/doc/review.md +++ b/doc/review.md @@ -67,10 +67,16 @@ gelesen), 3 (Fehler als eine Zeile, `RDB_DEBUG=1` für den Backtrace), 4 (`EDITO Argumenten, Editorfehler wird gemeldet), 5 (Satz wird in jedem Fall zurückgeschrieben), 6 (`Rdb.check_cells!`), 7 (`Rdb.write_file`: temporäre Datei plus `rename`) und 8 (Sortierschlüssel ohne Leerzeichen) sind behoben, jeweils mit Test. -Bei 9 ist nur `sorttable -r` behoben. Bewusst offen: `-nrf` gilt global (so beschreibt es -das Handbuch), `jointable -n` ist unklar spezifiziert, `1e3` als Zahl und die Zeile aus -leeren Zellen als „gelöscht“ sind Eigenheiten des Formats. Bei 8 bleibt `-x` (Faltung) beim -Index ohne Entsprechung in der Suche offen. + +Bei 9 sind `sorttable -r` (stabil) und `jointable -n` (Schlüssel als Zahlen, wie in der +Man-Page) behoben. Bleibt so (Entscheidung 2026-10-30): +- `-nrf` gilt für alle Spalten: laut Man-Page reicht `sorttable` die Optionen an `sort` + weiter, Optionen pro Spalte gibt es im Original nicht. +- `1e3` ist keine Zahl: sonst würden Spalten mit Codes wie `2E5` numerisch. +- Eine Zeile aus lauter leeren Zellen gilt als gelöscht: so arbeitet `delete` im Format. + +Offen bei 8: `index -x` (gefaltet) hat in `search` ohne `-x` kein Gegenstück, weil die +`.i`-Datei die Faltung nicht vermerkt. Das bräuchte eine Markierung im Indexkopf. ## Verbesserungspotential diff --git a/lib/rdb/cmd/relational.rb b/lib/rdb/cmd/relational.rb index 5f47fbe..0458ea2 100644 --- a/lib/rdb/cmd/relational.rb +++ b/lib/rdb/cmd/relational.rb @@ -96,7 +96,7 @@ end Rdb.register('jointable', 'Join two tables into one where keys match', 'jointable [-a[1|2]] [-j[1|2] column]... [-n] [-] table1 [table2]') do |args| - all1 = all2 = false + all1 = all2 = numeric = false k1 = k2 = nil files = [] until args.empty? @@ -108,7 +108,7 @@ Rdb.register('jointable', 'Join two tables into one where keys match', when '-j' then k1 = k2 = args.shift when '-j1' then k1 = args.shift when '-j2' then k2 = args.shift - when '-n' then nil + when '-n' then numeric = true else files << a end end @@ -119,13 +119,15 @@ Rdb.register('jointable', 'Join two tables into one where keys match', i1 = k1 ? t1.index_of(k1) : 0 i2 = k2 ? t2.index_of(k2) : 0 keep2 = (0...t2.head.size).to_a - [i2] + # -n compares keys as numbers: 01, 1 and " 1.0" are the same key + key = ->(v) { numeric && numeric?(v) ? Rdb.decimal(v) : v } by_key = Hash.new { |h, k| h[k] = [] } - t2.rows.each { |r| by_key[r[i2]] << r } + t2.rows.each { |r| by_key[key.(r[i2])] << r } used = {} out = [] t1.rows.each do |r| - if (m = by_key[r[i1]]).any? - used[r[i1]] = true + if (m = by_key.fetch(key.(r[i1]), [])).any? + used[key.(r[i1])] = true m.each { |r2| out << r + keep2.map { |i| r2[i] } } elsif all1 out << r + [''] * keep2.size @@ -133,7 +135,7 @@ Rdb.register('jointable', 'Join two tables into one where keys match', end if all2 t2.rows.each do |r2| - next if used[r2[i2]] + next if used[key.(r2[i2])] blank = [''] * t1.head.size blank[i1] = r2[i2] diff --git a/test/rdb_test.rb b/test/rdb_test.rb index 18c1a61..ae3b316 100644 --- a/test/rdb_test.rb +++ b/test/rdb_test.rb @@ -961,3 +961,13 @@ class ExactnessTest < RdbCase assert_equal 'v500', data_obj.row_at(data_obj.rows.last.from).cells[1] end end + +class JoinNumericTest < RdbCase + def test_jointable_n_matches_keys_as_numbers + write('j1', "K\tA\n-\t-\n01\tx\n 2\ty\n3\tz\n") + write('j2', "K\tB\n-\t-\n1\tp\n2.0\tq\n") + assert_empty data(rdb('jointable', 'j1', 'j2')) + assert_equal [%w[01 x p], [' 2', 'y', 'q']], data(rdb('jointable', '-n', 'j1', 'j2')) + assert_equal 3, data(rdb('jointable', '-n', '-a1', 'j1', 'j2')).size + end +end