класс CSV::Table
A CSV::Table — это двумерная структура данных для представления CSV документов. Таблицы позволяют работать с данными по строкам или столбцам, обрабатывать данные и даже преобразовывать результаты обратно в CSV, если необходимо.
Все таблицы, возвращаемые CSV, будут созданы из этого класса, если обработка заголовков включена.
Атрибуты
Текущий режим доступа для индексирования и итерации.
Внутренний формат данных, используемый для сравнения на равенство.
Открытые методы класса
# File lib/csv/table.rb, line 31
def initialize(array_of_rows, headers: nil)
@table = array_of_rows
@headers = headers
unless @headers
if @table.empty?
@headers = []
else
@headers = @table.first.headers
end
end
@mode = :col_or_row
end Создает новую CSV::Table из array_of_rows, которые должны быть объектами CSV::Row. Предполагается, что все строки имеют одинаковые заголовки.
Необязательный параметр headers может быть установлен в Array заголовков. Если заголовки не заданы, заголовки извлекаются из объектов CSV::Row. В противном случае метод headers() вернет заголовки, заданные в аргументе headers.
Объект CSV::Table поддерживает следующие методы Array через делегирование:
-
empty?()
-
length()
-
size()
Общедоступные методы экземпляра
# File lib/csv/table.rb, line 370
def <<(row_or_array)
if row_or_array.is_a? Array # append Array
@table << Row.new(headers, row_or_array)
else # append Row
@table << row_or_array
end
self # for chaining
end Если row_or_array является объектом CSV::Row, он добавляется в таблицу:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) table << CSV::Row.new(table.headers, ['bat', 3]) table[3] # => #<CSV::Row "Name":"bat" "Value":3>
Если row_or_array является массивом, он используется для создания нового объекта CSV::Row, который затем добавляется в таблицу:
table << ['bam', 4] table[4] # => #<CSV::Row "Name":"bam" "Value":4>
# File lib/csv/table.rb, line 563 def ==(other) return @table == other.table if other.is_a? CSV::Table @table == other end
Возвращает true , если все строки self == соответствуют соответствующим строкам other_table, в противном случае false.
Режим доступа не влияет на результат.
Равные таблицы:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) other_table = CSV.parse(source, headers: true) table == other_table # => true
Разное количество строк:
other_table.delete(2) table == other_table # => false
Разная последняя строка:
other_table << ['bat', 3] table == other_table # => false
# File lib/csv/table.rb, line 232
def [](index_or_header)
if @mode == :row or # by index
(@mode == :col_or_row and (index_or_header.is_a?(Integer) or index_or_header.is_a?(Range)))
@table[index_or_header]
else # by header
@table.map { |row| row[index_or_header] }
end
end Возвращает данные из таблицы; не изменяет таблицу.
Выражение table[n], где n — целое неотрицательное число, возвращает +n+‑ую строку таблицы, если такая строка существует, и если режим доступа равен :row или :col_or_row.
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) table.by_row! # => #<CSV::Table mode:row row_count:4> table[1] # => #<CSV::Row "Name":"bar" "Value":"1"> table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4> table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
Счёт идёт от последней строки, если n отрицательно:
table[-1] # => #<CSV::Row "Name":"baz" "Value":"2">
Возвращает nil , если n слишком большое или слишком маленькое:
table[4] # => nil table[-4] => nil
Вызывает исключение, если режим доступа равен :row и n не является объектом, преобразуемым в целое число.
table.by_row! # => #<CSV::Table mode:row row_count:4> # Raises TypeError (no implicit conversion of String into Integer): table['Name']
Выражение table[range], где range — объект Range, возвращает строки из таблицы, начиная со строки range.first, если эти строки существуют, и если режим доступа равен :row или :col_or_row:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) table.by_row! # => #<CSV::Table mode:row row_count:4> rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1"> rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">] table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4> rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1"> rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
Если строк недостаточно, возвращает все от range.first до конца:
rows = table[1..50] # => #<CSV::Row "Name":"bar" "Value":"1"> rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
Особый случай: если range.start == table.size, возвращает пустой массив:
table[table.size..50] # => []
Если range.end отрицательно, вычисляет конечный индекс с конца:
rows = table[0..-1] rows # => [#<CSV::Row "Name":"foo" "Value":"0">, #<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
Если range.start отрицательно, вычисляет начальный индекс с конца:
rows = table[-1..2] rows # => [#<CSV::Row "Name":"baz" "Value":"2">]
Если range.start больше table.size, возвращает nil:
table[4..4] # => nil
Выражение table[header], где header — строка, возвращает значения столбцов (массив строк), если столбец существует и если режим доступа равен :col или :col_or_row:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) table.by_col! # => #<CSV::Table mode:col row_count:4> table['Name'] # => ["foo", "bar", "baz"] table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4> col = table['Name'] col # => ["foo", "bar", "baz"]
Изменение возвращаемых значений столбцов не изменяет таблицу:
col[0] = 'bat' col # => ["bat", "bar", "baz"] table['Name'] # => ["foo", "bar", "baz"]
Возвращает массив значений nil, если такого столбца нет:
table['Nosuch'] # => [nil, nil, nil]
# File lib/csv/table.rb, line 257
def []=(index_or_header, value)
if @mode == :row or # by index
(@mode == :col_or_row and index_or_header.is_a? Integer)
if value.is_a? Array
@table[index_or_header] = Row.new(headers, value)
else
@table[index_or_header] = value
end
else # set column
unless index_or_header.is_a? Integer
index = @headers.index(index_or_header) || @headers.size
@headers[index] = index_or_header
end
if value.is_a? Array # multiple values
@table.each_with_index do |row, i|
if row.header_row?
row[index_or_header] = index_or_header
else
row[index_or_header] = value[i]
end
end
else # repeated value
@table.each do |row|
if row.header_row?
row[index_or_header] = index_or_header
else
row[index_or_header] = value
end
end
end
end
end В режиме смешанного доступа по умолчанию этот метод присваивает строки для индексного доступа и столбцы для доступа по заголовкам. Можно принудительно установить индексное отображение, предварительно вызвав by_col!() или by_row!().
Строки могут быть назначены массиву значений (которые унаследуют заголовки таблицы) или объекту CSV::Row.
Столбцы могут быть назначены одному значению, которое копируется в каждую строку столбца, или массиву значений. Массивы значений присваиваются строкам сверху вниз в порядке следования строк. Избыточные значения игнорируются, и если в массиве значений нет значения для каждой строки, дополнительные строки получат nil.
Присвоение существующему столбцу или строке перезаписывает данные. Присвоение новым столбцам создаёт их в правом конце таблицы.
# File lib/csv/table.rb, line 66 def by_col self.class.new(@table.dup).by_col! end
Возвращает дубликат объекта таблицы в режиме столбцов. Это удобно для цепочки вызовов без изменения режима таблицы, но имейте в виду, что этот метод может потреблять значительное количество памяти для больших наборов данных.
Этот метод возвращает дубликат таблицы для цепочки вызовов. Однако не цепляйте деструктивные методы (например, []=()) таким образом, так как вы работаете с дубликатом.
# File lib/csv/table.rb, line 76 def by_col! @mode = :col self end
Переключает режим этой таблицы на режим столбцов. Все вызовы методов индексирования и итерации будут работать со столбцами до тех пор, пока режим не будет изменён снова.
Этот метод возвращает таблицу и безопасен для цепочек вызовов.
# File lib/csv/table.rb, line 91 def by_col_or_row self.class.new(@table.dup).by_col_or_row! end
Возвращает дубликат объекта таблицы в режиме смешанного доступа. Это удобно для цепочки вызовов без изменения режима таблицы, но имейте в виду, что этот метод может потреблять значительное количество памяти для больших наборов данных.
Этот метод возвращает дубликат таблицы для цепочки вызовов. Однако не цепляйте деструктивные методы (например, []=()) таким образом, так как вы работаете с дубликатом.
# File lib/csv/table.rb, line 103 def by_col_or_row! @mode = :col_or_row self end
Переключает режим этой таблицы на смешанный режим. Все вызовы методов индексирования и итерации будут использовать стандартную интеллектуальную систему индексирования до тех пор, пока режим не будет изменён снова. В режиме смешанного доступа предполагается, что индекс — ссылка на строку, а всё остальное — доступ к столбцу по заголовкам.
Этот метод возвращает таблицу и безопасен для цепочек вызовов.
# File lib/csv/table.rb, line 118 def by_row self.class.new(@table.dup).by_row! end
Возвращает дубликат объекта таблицы в режиме строк. Это удобно для цепочки вызовов без изменения режима таблицы, но имейте в виду, что этот метод может потреблять значительное количество памяти для больших наборов данных.
Этот метод возвращает дубликат таблицы для цепочки вызовов. Однако не цепляйте деструктивные методы (например, []=()) таким образом, так как вы работаете с дубликатом.
# File lib/csv/table.rb, line 128 def by_row! @mode = :row self end
Переключает режим этой таблицы на режим строк. Все вызовы методов индексирования и итерации будут работать со строками до тех пор, пока режим не будет изменён снова.
Этот метод возвращает таблицу и безопасен для цепочек вызовов.
# File lib/csv/table.rb, line 442
def delete(*indexes_or_headers)
if indexes_or_headers.empty?
raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
end
deleted_values = indexes_or_headers.map do |index_or_header|
if @mode == :row or # by index
(@mode == :col_or_row and index_or_header.is_a? Integer)
@table.delete_at(index_or_header)
else # by header
if index_or_header.is_a? Integer
@headers.delete_at(index_or_header)
else
@headers.delete(index_or_header)
end
@table.map { |row| row.delete(index_or_header).last }
end
end
if indexes_or_headers.size == 1
deleted_values[0]
else
deleted_values
end
end Если режим доступа равен :row или :col_or_row, и каждый аргумент является целым числом или диапазоном, возвращает удалённые строки. В противном случае возвращает данные удалённых столбцов.
В любом случае возвращаемые значения находятся в порядке, указанном аргументами. Аргументы могут повторяться.
Возвращает строки в виде массива объектов CSV::Row.
Один индекс:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) deleted_values = table.delete(0) deleted_values # => [#<CSV::Row "Name":"foo" "Value":"0">]
Два индекса:
table = CSV.parse(source, headers: true) deleted_values = table.delete(2, 0) deleted_values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
Возвращает данные столбцов в виде массивов столбцов.
Один заголовок:
table = CSV.parse(source, headers: true)
deleted_values = table.delete('Name')
deleted_values # => ["foo", "bar", "baz"]
Два заголовка:
table = CSV.parse(source, headers: true)
deleted_values = table.delete('Value', 'Name')
deleted_values # => [["0", "1", "2"], ["foo", "bar", "baz"]]
# File lib/csv/table.rb, line 492
def delete_if(&block)
return enum_for(__method__) { @mode == :row or @mode == :col_or_row ? size : headers.size } unless block_given?
if @mode == :row or @mode == :col_or_row # by index
@table.delete_if(&block)
else # by header
deleted = []
headers.each do |header|
deleted << delete(header) if yield([header, self[header]])
end
end
self # for chaining
end Удаляет строки или столбцы, для которых блок возвращает истинное значение; возвращает self.
Удаляет строки, когда режим доступа равен :row или :col_or_row; вызывает блок с каждым объектом CSV::Row:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(source, headers: true)
table.by_row! # => #<CSV::Table mode:row row_count:4>
table.size # => 3
table.delete_if {|row| row['Name'].start_with?('b') }
table.size # => 1
Удаляет столбцы, когда режим доступа равен :col; вызывает блок с каждым столбцом как массивом из 2 элементов, содержащим заголовок и массив полей столбца:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(source, headers: true)
table.by_col! # => #<CSV::Table mode:col row_count:4>
table.headers.size # => 2
table.delete_if {|column_data| column_data[1].include?('2') }
table.headers.size # => 1
Возвращает новый перечислитель, если блок не задан:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) table.delete_if # => #<Enumerator: #<CSV::Table mode:col_or_row row_count:4>:delete_if>
# File lib/csv/table.rb, line 602
def dig(index_or_header, *index_or_headers)
value = self[index_or_header]
if value.nil?
nil
elsif index_or_headers.empty?
value
else
unless value.respond_to?(:dig)
raise TypeError, "#{value.class} does not have \#dig method"
end
value.dig(*index_or_headers)
end
end Извлекает вложенное значение, указанное последовательностью объектов index или header, вызывая dig на каждом шаге, возвращая nil, если какой-либо промежуточный шаг имеет значение nil.
# File lib/csv/table.rb, line 533
def each(&block)
return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
if @mode == :col
headers.each { |header| yield([header, self[header]]) }
else
@table.each(&block)
end
self # for chaining
end Вызывает блок для каждой строки или столбца; возвращает self.
При режиме доступа :row или :col_or_row, вызывает блок для каждого объекта CSV::Row:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(source, headers: true)
table.by_row! # => #<CSV::Table mode:row row_count:4>
table.each {|row| p row }
Вывод:
#<CSV::Row "Name":"foo" "Value":"0"> #<CSV::Row "Name":"bar" "Value":"1"> #<CSV::Row "Name":"baz" "Value":"2">
При режиме доступа :col, вызывает блок для каждого столбца как массив из двух элементов, содержащий заголовок и массив полей столбца:
table.by_col! # => #<CSV::Table mode:col row_count:4>
table.each {|column_data| p column_data }
Вывод:
["Name", ["foo", "bar", "baz"]] ["Value", ["0", "1", "2"]]
Возвращает новый Enumerator, если блок не указан:
table.each # => #<Enumerator: #<CSV::Table mode:col row_count:4>:each>
# File lib/csv/table.rb, line 139
def headers
if @table.empty?
@headers.dup
else
@table.first.headers
end
end Возвращает заголовки для первой строки этой таблицы (предполагается, что они соответствуют всем другим строкам). Заголовки Array, переданные в CSV::Table.new, возвращаются для пустых таблиц.
# File lib/csv/table.rb, line 617
def inspect
"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
end Показывает режим и размер этой таблицы в US-ASCII String.
# File lib/csv/table.rb, line 396
def push(*rows)
rows.each { |row| self << row }
self # for chaining
end Является сокращением для добавления нескольких строк. Эквивалентно:
rows.each {|row| self << row }
Каждый аргумент может быть объектом CSV::Row или массивом:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) rows = [ CSV::Row.new(table.headers, ['bat', 3]), ['bam', 4] ] table.push(*rows) table[3..4] # => [#<CSV::Row "Name":"bat" "Value":3>, #<CSV::Row "Name":"bam" "Value":4>]
# File lib/csv/table.rb, line 572
def to_a
array = [headers]
@table.each do |row|
array.push(row.fields) unless row.header_row?
end
array
end Возвращает таблицу как Array массивов. Заголовки будут первой строкой, затем последуют все строки данных.
# File lib/csv/table.rb, line 588
def to_csv(write_headers: true, **options)
array = write_headers ? [headers.to_csv(**options)] : []
@table.each do |row|
array.push(row.fields.to_csv(**options)) unless row.header_row?
end
array.join("")
end Возвращает таблицу как полный CSV String. Заголовки будут перечислены первыми, затем все строки данных.
Этот метод предполагает, что вы хотите Table.headers(), если вы явно не передадите :write_headers => false.
# File lib/csv/table.rb, line 342
def values_at(*indices_or_headers)
if @mode == :row or # by indices
( @mode == :col_or_row and indices_or_headers.all? do |index|
index.is_a?(Integer) or
( index.is_a?(Range) and
index.first.is_a?(Integer) and
index.last.is_a?(Integer) )
end )
@table.values_at(*indices_or_headers)
else # by headers
@table.map { |row| row.values_at(*indices_or_headers) }
end
end Если режим доступа :row или :col_or_row, и каждый аргумент — целое число или диапазон, возвращает строки. В противном случае возвращает данные столбцов.
В любом случае, возвращаемые значения упорядочены в соответствии с аргументами. Аргументы могут повторяться.
Возвращает строки как массив объектов CSV::Row.
Без аргументов:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) table.values_at # => []
Один индекс:
values = table.values_at(0) values # => [#<CSV::Row "Name":"foo" "Value":"0">]
Два индекса:
values = table.values_at(2, 0) values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
Один диапазон:
values = table.values_at(1..2) values # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
Диапазоны и индексы:
values = table.values_at(0..1, 1..2, 0, 2) pp values
Вывод:
[#<CSV::Row "Name":"foo" "Value":"0">, #<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">, #<CSV::Row "Name":"baz" "Value":"2">]
Возвращает данные столбцов как массивы строк, каждый из которых содержит данные указанных столбцов для этой строки:
values = table.values_at('Name')
values # => [["foo"], ["bar"], ["baz"]]
values = table.values_at('Value', 'Name')
values # => [["0", "foo"], ["1", "bar"], ["2", "baz"]]
Ruby Core © 1993–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.