From 17a86d43d7a03c7e78819656334858e1bce66f53 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Thu, 22 Jun 2023 15:27:35 +0000 Subject: [PATCH 1/2] - Add support for accessing rows by `table[colname: value]` --- Runfile | 9 ++--- lib/true_table.rb | 54 +++++++++++++++++++++++------- spec/true_table/true_table_spec.rb | 27 +++++++++++++++ 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/Runfile b/Runfile index 156867c..d105479 100644 --- a/Runfile +++ b/Runfile @@ -1,8 +1,9 @@ -require "byebug" -require "lp" +require 'byebug' +require 'lp' +require 'true_table' -title "TrueTable Developer Toolbelt" -summary "Runfile tasks for building the TrueTable gem" +title 'TrueTable Developer Toolbelt' +summary 'Runfile tasks for building the TrueTable gem' import_gem 'runfile-tasks/gem' import_gem 'runfile-tasks/yard' diff --git a/lib/true_table.rb b/lib/true_table.rb index bf37c3c..8109928 100644 --- a/lib/true_table.rb +++ b/lib/true_table.rb @@ -51,12 +51,20 @@ def -(other) # Returns a row or a column def [](key) - key.is_a?(Symbol) ? col(key) : super + case key + when Symbol then col(key) + when Hash then row(key) + else super + end end # Adds or updates a row or a column def []=(key, value) - key.is_a?(Symbol) ? add_col(key.to_sym, value) : super + case key + when Symbol then add_col(key.to_sym, value) + when Hash then add_row(key, value) + else super + end end # Returns a deep copy of self @@ -134,19 +142,27 @@ def each_col # Iterates over rows alias each_row each_with_index + # Returns a row or a column def fetch(key, *default) - if key.is_a?(Symbol) - if headers.include? key - col(key.to_sym) - elsif default.any? - default.first - elsif block_given? - yield key - else - raise IndexError, "row :#{key} does not exist" + case key + when Symbol + if headers.include?(key) then col(key.to_sym) + elsif default.any? then default.first + elsif block_given? then yield key + else raise IndexError, "col :#{key} does not exist" end + + when Hash + result = row(key) + if result then result + elsif default.any? then default.first + elsif block_given? then yield key + else raise IndexError, "row #{key} does not exist" + end + else super + end end @@ -155,6 +171,11 @@ def headers first.keys end + # Returns a short inspection string + def inspect + "<#{self.class}:#{object_id} size=#{size} headers=#{headers}>" + end + # Returns a new table with intersecting rows def intersection(*others) self.class.new super @@ -191,7 +212,12 @@ def rotate(count = 1) end # Returns a row - alias row [] + def row(key) + key.is_a?(Hash) ? find { |x| x[key.first.first] == key.first.last } : self[key] + end + + # Returns all rows + alias rows to_a # Saves the table as a CSV file def save_csv(path) @@ -286,4 +312,8 @@ def add_col(key, values) self[i][key] = value end end + + def add_row(key, values) + row(key).merge! values + end end diff --git a/spec/true_table/true_table_spec.rb b/spec/true_table/true_table_spec.rb index f1e33ae..51a5977 100644 --- a/spec/true_table/true_table_spec.rb +++ b/spec/true_table/true_table_spec.rb @@ -86,6 +86,12 @@ expect(subject[:population][3]).to eq 2 end end + + context 'with a hash key' do + it 'returns a row' do + expect(subject[year: 2023][:population]).to eq 2 + end + end end describe '#[]=' do @@ -102,6 +108,13 @@ expect(subject[1]).to eq({ population: 20_000, year: 2021, infected: 1_980_000 }) end end + + context 'with a hash key' do + it 'adds the row' do + subject[year: 2023] = { population: 23 } + expect(subject[:population]).to eq [2_000_000, 20_000, 200, 23] + end + end end describe '#clone' do @@ -527,6 +540,20 @@ end end + describe '#row' do + context 'with an integer key' do + it 'returns a row' do + expect(subject[2][:population]).to eq 200 + end + end + + context 'with a hash key' do + it 'returns a row' do + expect(subject[year: 2023][:population]).to eq 2 + end + end + end + describe '#rotate' do it 'offsets the rows to the top' do expect(subject.rotate(2)[0][:year]).to eq 2022 From cc25630ae51dbcc210d482b2bf1e3bdd9587cd5b Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Thu, 22 Jun 2023 15:28:29 +0000 Subject: [PATCH 2/2] rubocop --- lib/true_table.rb | 8 +++++--- spec/true_table/true_table_spec.rb | 6 ++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/true_table.rb b/lib/true_table.rb index 8109928..ef1eae0 100644 --- a/lib/true_table.rb +++ b/lib/true_table.rb @@ -149,7 +149,8 @@ def fetch(key, *default) if headers.include?(key) then col(key.to_sym) elsif default.any? then default.first elsif block_given? then yield key - else raise IndexError, "col :#{key} does not exist" + else + raise IndexError, "col :#{key} does not exist" end when Hash @@ -157,12 +158,13 @@ def fetch(key, *default) if result then result elsif default.any? then default.first elsif block_given? then yield key - else raise IndexError, "row #{key} does not exist" + else + raise IndexError, "row #{key} does not exist" end else super - + end end diff --git a/spec/true_table/true_table_spec.rb b/spec/true_table/true_table_spec.rb index 51a5977..56b671b 100644 --- a/spec/true_table/true_table_spec.rb +++ b/spec/true_table/true_table_spec.rb @@ -452,9 +452,7 @@ describe '#minmax' do it 'returns a two element array with min and max rows' do - result = subject.minmax do |a, b| - a[:population] <=> b[:population] - end + result = subject.minmax_by { |a| a[:population] } expect(result[0][:year]).to eq 2023 expect(result[1][:year]).to eq 2020 end @@ -681,7 +679,7 @@ describe '#sort!' do it 'sorts the table in place' do - subject.sort! { |a, b| a[:population] <=> b[:population] } + subject.sort_by! { |a| a[:population] } expect(subject[0][:population]).to eq 2 expect(subject[3][:population]).to eq 2_000_000