diff --git a/README.md b/README.md index 1188326..0cbb860 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,12 @@ Xsv has two modes of operation. By default, it returns an array for each row in the sheet: ```ruby -x = Xsv.open("sheet.xlsx") # => # +workbook = Xsv.open("sheet.xlsx") # => # -sheet = x.sheets[0] +# Access worksheet by index, 0 is the first sheet +sheet = workbook[0] +# or, access worksheet by name +sheet = workbook["Sheet1"] # Iterate over rows sheet.each do |row| @@ -65,15 +68,16 @@ option on open: ```ruby # Parse headers for all sheets on open -x = Xsv.open("sheet.xlsx", parse_headers: true) +workbook = Xsv.open("sheet.xlsx", parse_headers: true) -x.sheets[0][1] # => {"header1" => "value1", "header2" => "value2"} +# Get the first row from the first sheet +workbook.first.first # => {"header1" => "value1", "header2" => "value2"} # Manually parse headers for a single sheet -x = Xsv.open("sheet.xlsx") +workbook = Xsv.open("sheet.xlsx") -sheet = x.sheets[0] +sheet = workbook[0] sheet[0] # => ["header1", "header2"] @@ -86,7 +90,13 @@ Xsv will raise `Xsv::DuplicateHeaders` if it detects duplicate values in the hea `#parse_headers!` or when opening a workbook with `parse_headers: true` to ensure hash keys are unique. `Xsv::Sheet` implements `Enumerable` so along with `#each` -you can call methods like `#first`, `#filter`/`#select`, and `#map` on it. +you can call methods like `#first`, `#filter`/`#select`, and `#map` on it. Likewise these methods can +be used on `Xsv::Workbook` to iterate over sheets, for example: + +```ruby +# Get the name of all the sheets in a workbook +sheet_names = @workbook.map(&:name) +``` ### Opening a string or buffer instead of filename @@ -112,24 +122,6 @@ end Prior to Xsv 1.1.0, `Xsv::Workbook.open` was used instead of `Xsv.open`. The parameters are identical and the former is maintained for backwards compatibility. -### Accessing sheets by name - -The sheets can be accessed by index or by name: - -```ruby -x = Xsv.open("sheet.xlsx") - -sheet = x.sheets[0] # gets sheet by index - -sheet = x.sheets_by_name('Name').first # gets sheet by name -``` - -To get all the sheets names: - -```ruby -sheet_names = x.sheets.map(&:name) -``` - ### Assumptions Since Xsv treats worksheets like csv files it makes certain assumptions about your diff --git a/lib/xsv/workbook.rb b/lib/xsv/workbook.rb index 5bcb285..e875f5b 100644 --- a/lib/xsv/workbook.rb +++ b/lib/xsv/workbook.rb @@ -6,6 +6,8 @@ module Xsv # An OOXML Spreadsheet document is called a Workbook. A Workbook consists of # multiple Sheets that are available in the array that's accessible through {#sheets} class Workbook + include Enumerable + # Access the Sheet objects contained in the workbook # @return [Array] attr_reader :sheets @@ -69,6 +71,24 @@ def get_num_fmt(style) @num_fmts[@xfs[style][:numFmtId]] end + def each(&block) + sheets.each(&block) + end + + # Get a sheet by index or name + # @param [String, Integer] index_or_name The name of the sheet or index in the workbook + # @return [, nil] returns the sheet instance or nil if it was not found + def [](index_or_name) + case index_or_name + when Integer + sheets[index_or_name] + when String + sheets_by_name(index_or_name).first + else + raise ArgumentError, "Sheets can be accessed by Integer of String only" + end + end + private def fetch_shared_strings diff --git a/test/workbook_test.rb b/test/workbook_test.rb index 6c1fa66..cb0cf08 100644 --- a/test/workbook_test.rb +++ b/test/workbook_test.rb @@ -112,4 +112,52 @@ def test_open_xml_sdk assert_equal "2022-03-04 12:00:00 AM", @sheet[0]["Date"] assert_equal "ABC123", @sheet[0]["Value"] end + + def test_index_open_by_index + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + sheet = @workbook[1] + assert_equal "Blad2", sheet.name + end + + def test_index_open_by_out_of_range_index + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + sheet = @workbook[99] + assert_nil sheet + end + + def test_index_open_by_name + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + sheet = @workbook["Blad2"] + assert_equal "Blad2", sheet.name + end + + def test_index_open_by_nonexistent_name + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + sheet = @workbook["Blad99"] + assert_nil sheet + end + + def test_index_open_by_invalid_type + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + assert_raises ArgumentError do + @workbook[true] + end + end + + def test_first + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + assert_equal "Blad1", @workbook.first.name + end + + def test_enumerable + @workbook = Xsv.open(File.read("test/files/office365-xl7.xlsx")) + + assert_equal %w(Blad1 Blad2 Blad3), @workbook.map(&:name) + end end