класс 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 не является объектом, преобразуемым к типу Integer.
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; вызывает блок с каждым столбцом в виде массива из двух элементов, содержащего заголовок и массив значений столбца:
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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.