класс CSV::Row
CSV::Row
Экземпляр CSV::Row представляет собой строку CSV-таблицы. (см. класс CSV).
Экземпляр может содержать:
-
Поля: каждое — объект, необязательно строка.
-
Заголовки: каждый служит ключом, и также необязательно строкой.
Методы экземпляра
CSV::Row имеет три группы методов экземпляра:
-
Свои внутренние методы экземпляра.
-
Методы, включенные модулем
Enumerable. -
Методы, делегированные классу
Array.:
Создание экземпляра CSV::Row
Обычно новый экземпляр CSV::Row создаётся путём парсинга CSV-источника, содержащего заголовки:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(source, headers: true)
table.each {|row| p row }
Вывод:
#<CSV::Row "Name":"foo" "Value":"0"> #<CSV::Row "Name":"bar" "Value":"1"> #<CSV::Row "Name":"baz" "Value":"2">
Вы также можете создать строку напрямую. См. ::new.
Заголовки
Как и CSV::Table, CSV::Row имеет заголовки.
CSV::Row, созданный путём парсинга CSV-источника, наследует заголовки от таблицы:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table.first row.headers # => ["Name", "Value"]
Вы также можете создать новую строку с заголовками; как и ключи в Hash, заголовки необязательно должны быть строками:
row = CSV::Row.new([:name, :value], ['foo', 0]) row.headers # => [:name, :value]
Новая строка сохраняет свои заголовки, даже если она добавлена в таблицу, которая имеет заголовки:
table << row # => #<CSV::Table mode:col_or_row row_count:5> row.headers # => [:name, :value] row[:name] # => "foo" row['Name'] # => nil
Доступ к полям
Вы можете получить доступ к полю в CSV::Row с помощью его целочисленного индекса (стиль массива) или заголовка (стиль хэша).
Получение поля с помощью метода []:
row = CSV::Row.new(['Name', 'Value'], ['foo', 0]) row[1] # => 0 row['Value'] # => 0
Set поля с помощью метода []=:
row = CSV::Row.new(['Name', 'Value'], ['foo', 0]) row # => #<CSV::Row "Name":"foo" "Value":0> row[0] = 'bar' row['Value'] = 1 row # => #<CSV::Row "Name":"bar" "Value":1>
Атрибуты
Внутренний формат данных, используемый для сравнения на равенство.
Публичные методы класса
# File lib/csv/row.rb, line 105
def initialize(headers, fields, header_row = false)
@header_row = header_row
headers.each { |h| h.freeze if h.is_a? String }
# handle extra headers or fields
@row = if headers.size >= fields.size
headers.zip(fields)
else
fields.zip(headers).each(&:reverse!)
end
end Возвращает новый экземпляр CSV::Row, созданный из аргументов headers и fields; оба должны быть массивами; обратите внимание, что поля необязательно должны быть строками:
row = CSV::Row.new(['Name', 'Value'], ['foo', 0]) row # => #<CSV::Row "Name":"foo" "Value":0>
Если длины массивов различаются, более короткий заполняется nil значениями:
row = CSV::Row.new(['Name', 'Value', 'Date', 'Size'], ['foo', 0]) row # => #<CSV::Row "Name":"foo" "Value":0 "Date":nil "Size":nil>
Каждый объект CSV::Row является либо строкой полей, либо строкой заголовков; по умолчанию новый ряд — это строка полей; для созданной выше строки:
row.field_row? # => true row.header_row? # => false
Если необязательный аргумент header_row задан как true, создаваемая строка — строка заголовков:
row = CSV::Row.new(['Name', 'Value'], ['foo', 0], header_row = true) row # => #<CSV::Row "Name":"foo" "Value":0> row.field_row? # => false row.header_row? # => true
Публичные методы экземпляра
# File lib/csv/row.rb, line 389
def <<(arg)
if arg.is_a?(Array) and arg.size == 2 # appending a header and name
@row << arg
elsif arg.is_a?(Hash) # append header and name pairs
arg.each { |pair| @row << pair }
else # append field value
@row << [nil, arg]
end
self # for chaining
end Добавляет поле в self; возвращает self:
Если аргумент является 2-элементным массивом [header, value], добавляется поле с заданным header и value:
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] row << ['NAME', 'Bat'] row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" "NAME":"Bat">
Если аргумент является хэшем, каждая пара key-value добавляется как поле с заголовком key и значением value.
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row << {NAME: 'Bat', name: 'Bam'}
row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" NAME:"Bat" name:"Bam">
В противном случае, заданное value добавляется как поле без заголовка.
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] row << 'Bag' row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" nil:"Bag">
# File lib/csv/row.rb, line 633 def ==(other) return @row == other.row if other.is_a? CSV::Row @row == other end
Возвращает true, если other является /CSV::Row, имеющим те же поля (заголовки и значения) в том же порядке, что и self; в противном случае возвращает false:
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] other_row = table[0] row == other_row # => true other_row = table[1] row == other_row # => false
# File lib/csv/row.rb, line 339
def []=(*args)
value = args.pop
if args.first.is_a? Integer
if @row[args.first].nil? # extending past the end with index
@row[args.first] = [nil, value]
@row.map! { |pair| pair.nil? ? [nil, nil] : pair }
else # normal index assignment
@row[args.first][1] = value
end
else
index = index(*args)
if index.nil? # appending a field
self << [args.first, value]
else # normal header assignment
@row[index][1] = value
end
end
end Присваивает значение поля для данного index или header; возвращает value.
Присвоение значения поля по целочисленному индексу:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table[0] row[0] = 'Bat' row[1] = 3 row # => #<CSV::Row "Name":"Bat" "Value":3>
Считает назад от последнего столбца, если index отрицателен:
row[-1] = 4 row[-2] = 'Bam' row # => #<CSV::Row "Name":"Bam" "Value":4>
Расширяет строку с nil:nil, если положительное index отсутствует в строке:
row[4] = 5 row # => #<CSV::Row "Name":"bad" "Value":4 nil:nil nil:nil nil:5>
Вызывает IndexError, если отрицательное index слишком мало (слишком далеко от нуля).
Присвоение значения поля по заголовку (первое найденное):
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] row['Name'] = 'Bat' row # => #<CSV::Row "Name":"Bat" "Name":"Bar" "Name":"Baz">
Присвоение значения поля по заголовку, игнорируя offset ведущих полей:
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] row['Name', 2] = 4 row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":4>
Добавление нового поля по (новому) заголовку:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table[0] row['New'] = 6 row# => #<CSV::Row "Name":"foo" "Value":"0" "New":6>
# File lib/csv/row.rb, line 682 def deconstruct fields end
Возвращает новый массив, подходящий для сопоставления шаблонов, содержащий значения строки.
# File lib/csv/row.rb, line 667
def deconstruct_keys(keys)
if keys.nil?
to_h
else
keys.to_h { |key| [key, self[key]] }
end
end Возвращает новый хэш, подходящий для сопоставления шаблонов, содержащий только ключи, указанные в качестве аргумента.
# File lib/csv/row.rb, line 451
def delete(header_or_index, minimum_index = 0)
if header_or_index.is_a? Integer # by index
@row.delete_at(header_or_index)
elsif i = index(header_or_index, minimum_index) # by header
@row.delete_at(i)
else
[ ]
end
end Удаляет указанное поле из self; возвращает 2-элементный массив [header, value], если поле существует.
Если задан целочисленный аргумент index, удаляет и возвращает поле по смещению index, или возвращает nil, если поле не существует:
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] row.delete(1) # => ["Name", "Bar"] row.delete(50) # => nil
В противном случае, если задан единственный аргумент header, удаляет и возвращает первое найденное поле с заданным заголовком, или возвращает новый пустой массив, если поле не существует:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.delete('Name') # => ["Name", "Foo"]
row.delete('NAME') # => []
Если заданы аргумент header и целочисленный аргумент offset, удаляет и возвращает первое найденное поле с заданным заголовком, смещение которого не меньше, чем offset:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.delete('Name', 1) # => ["Name", "Bar"]
row.delete('NAME', 1) # => []
# File lib/csv/row.rb, line 476
def delete_if(&block)
return enum_for(__method__) { size } unless block_given?
@row.delete_if(&block)
self # for chaining
end Удаляет поля из self по выбору блока; возвращает self.
Удаляет каждое поле, для которого блок возвращает истинное значение:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.delete_if {|header, value| value.start_with?('B') } # => true
row # => #<CSV::Row "Name":"Foo">
row.delete_if {|header, value| header.start_with?('B') } # => false
Если блок не задан, возвращает новый перечислитель:
row.delete_if # => #<Enumerator: #<CSV::Row "Name":"Foo">:delete_if>
# File lib/csv/row.rb, line 715
def dig(index_or_header, *indexes)
value = field(index_or_header)
if value.nil?
nil
elsif indexes.empty?
value
else
unless value.respond_to?(:dig)
raise TypeError, "#{value.class} does not have \#dig method"
end
value.dig(*indexes)
end
end Находит и возвращает объект во вложенном объекте, указанный index_or_header и specifiers.
Вложенные объекты могут быть экземплярами различных классов. См. Dig Methods.
Примеры:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.dig(1) # => "0"
row.dig('Value') # => "0"
row.dig(5) # => nil
# File lib/csv/row.rb, line 610
def each(&block)
return enum_for(__method__) { size } unless block_given?
@row.each(&block)
self # for chaining
end Вызывает блок с каждой парой заголовок-значение; возвращает self:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.each {|header, value| p [header, value] }
Вывод:
["Name", "Foo"] ["Name", "Bar"] ["Name", "Baz"]
Если блок не задан, возвращает новый перечислитель:
row.each # => #<Enumerator: #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz">:each>
# File lib/csv/row.rb, line 258
def fetch(header, *varargs)
raise ArgumentError, "Too many arguments" if varargs.length > 1
pair = @row.assoc(header)
if pair
pair.last
else
if block_given?
yield header
elsif varargs.empty?
raise KeyError, "key not found: #{header}"
else
varargs.first
end
end
end Возвращает значение поля, указанное header.
С единственным аргументом header, возвращает значение поля для этого заголовка (первое найденное):
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.fetch('Name') # => "Foo"
Вызывает исключение KeyError, если заголовок не существует.
С аргументами header и default, возвращает значение поля для заголовка (первое найденное), если заголовок существует, в противном случае возвращает default:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.fetch('Name', '') # => "Foo"
row.fetch(:nosuch, '') # => ""
С аргументом header и заданным блоком, возвращает значение поля для заголовка (первое найденное), если заголовок существует; в противном случае вызывает блок и возвращает его возвращаемое значение:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.fetch('Name') {|header| fail 'Cannot happen' } # => "Foo"
row.fetch(:nosuch) {|header| "Header '#{header} not found'" } # => "Header 'nosuch not found'"
# File lib/csv/row.rb, line 203
def field(header_or_index, minimum_index = 0)
# locate the pair
finder = (header_or_index.is_a?(Integer) || header_or_index.is_a?(Range)) ? :[] : :assoc
pair = @row[minimum_index..-1].public_send(finder, header_or_index)
# return the field if we have a pair
if pair.nil?
nil
else
header_or_index.is_a?(Range) ? pair.map(&:last) : pair.last
end
end Возвращает значение поля для данного index или header.
Получение значения поля по целочисленному индексу:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table[0] row.field(0) # => "foo" row.field(1) # => "bar"
Считает назад от последнего столбца, если index отрицателен:
row.field(-1) # => "0" row.field(-2) # => "foo"
Возвращает nil, если index находится вне диапазона:
row.field(2) # => nil row.field(-3) # => nil
Получение значения поля по заголовку (первое найденное):
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.field('Name') # => "Foo"
Получение значения поля по заголовку, игнорируя offset ведущих полей:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.field('Name', 2) # => "Baz"
Возвращает nil, если заголовок не существует.
# File lib/csv/row.rb, line 589 def field?(data) fields.include? data end
Возвращает true, если value является полем в этой строке, false в противном случае:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.field?('Bar') # => true
row.field?('BAR') # => false
# File lib/csv/row.rb, line 148 def field_row? not header_row? end
Возвращает true, если это строка поля, и false в противном случае.
# File lib/csv/row.rb, line 530
def fields(*headers_and_or_indices)
if headers_and_or_indices.empty? # return all fields--no arguments
@row.map(&:last)
else # or work like values_at()
all = []
headers_and_or_indices.each do |h_or_i|
if h_or_i.is_a? Range
index_begin = h_or_i.begin.is_a?(Integer) ? h_or_i.begin :
index(h_or_i.begin)
index_end = h_or_i.end.is_a?(Integer) ? h_or_i.end :
index(h_or_i.end)
new_range = h_or_i.exclude_end? ? (index_begin...index_end) :
(index_begin..index_end)
all.concat(fields.values_at(new_range))
else
all << field(*Array(h_or_i))
end
end
return all
end
end Возвращает значения полей в соответствии с заданными спецификаторами, которые могут быть любой комбинацией:
-
Целочисленный индекс.
-
Диапазон целочисленных индексов.
-
Массив из двух элементов, содержащий заголовок и смещение.
-
Заголовок.
-
Диапазон заголовков.
Для спецификатора в одном из первых четырёх случаев выше, возвращает результат self.field(specifier); см. field.
Хотя может быть любое количество спецификаторов, примеры ниже иллюстрируют по одному за раз.
Если спецификатор — целое число index, возвращает self.field(index)L
source = "Name,Name,Name\nFoo,Bar,Baz\n" table = CSV.parse(source, headers: true) row = table[0] row.fields(1) # => ["Bar"]
Если спецификатор — диапазон целых чисел range, возвращает self.field(range):
row.fields(1..2) # => ["Bar", "Baz"]
Если спецификатор — массив из двух элементов array, возвращает self.field(array)L
row.fields('Name', 1) # => ["Foo", "Bar"]
Если спецификатор — заголовок header, возвращает self.field(header)L
row.fields('Name') # => ["Foo"]
Если спецификатор — диапазон заголовков range, формирует новый диапазон new_range из индексов range.start и range.end, и возвращает self.field(new_range):
source = "Name,NAME,name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.fields('Name'..'NAME') # => ["Foo", "Bar"]
Возвращает все поля, если аргумент не указан:
row.fields # => ["Foo", "Bar", "Baz"]
# File lib/csv/row.rb, line 279 def has_key?(header) !!@row.assoc(header) end
Возвращает true, если существует поле с заданным заголовком, и false в противном случае.
# File lib/csv/row.rb, line 140 def header_row? @header_row end
Возвращает true, если это строка заголовка, и false в противном случае.
# File lib/csv/row.rb, line 160 def headers @row.map(&:first) end
Возвращает заголовки для этой строки:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table.first row.headers # => ["Name", "Value"]
# File lib/csv/row.rb, line 573 def index(header, minimum_index = 0) # find the pair index = headers[minimum_index..-1].index(header) # return the index at the right offset, if we found one index.nil? ? nil : index + minimum_index end
Возвращает индекс заданного заголовка, если он существует; в противном случае возвращает nil.
С одним аргументом header, возвращает индекс первого найденного поля с заданным заголовком:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.index('Name') # => 0
row.index('NAME') # => nil
С аргументами header и offset, возвращает индекс первого найденного поля с заданным заголовком, но игнорирует первые offset поля:
row.index('Name', 1) # => 1
row.index('Name', 3) # => nil
# File lib/csv/row.rb, line 130 def initialize_copy(other) super_return_value = super @row = @row.collect(&:dup) super_return_value end
Вызывает метод суперкласса.
# File lib/csv/row.rb, line 740
def inspect
str = ["#<", self.class.to_s]
each do |header, field|
str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) <<
":" << field.inspect
end
str << ">"
begin
str.join('')
rescue # any encoding error
str.map do |s|
e = Encoding::Converter.asciicompat_encoding(s.encoding)
e ? s.encode(e) : s.force_encoding("ASCII-8BIT")
end.join('')
end
end Возвращает строку, совместимую с ASCII, отображающую:
-
ClassCSV::Row. -
Пары заголовок-значение.
Пример:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table[0] row.inspect # => "#<CSV::Row \"Name\":\"foo\" \"Value\":\"0\">"
# File lib/csv/row.rb, line 410
def push(*args)
args.each { |arg| self << arg }
self # for chaining
end Добавляет каждое из переданных значений в качестве поля к текущей строке; возвращает self:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.push('Bat', 'Bam')
row # => #<CSV::Row "Name":"Foo" "Name":"Bar" "Name":"Baz" nil:"Bat" nil:"Bam">
# File lib/csv/row.rb, line 694 def to_csv(**options) fields.to_csv(**options) end
Возвращает строку в формате CSV String. Заголовки не включаются:
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n" table = CSV.parse(source, headers: true) row = table[0] row.to_csv # => "foo,0\n"
# File lib/csv/row.rb, line 653
def to_h
hash = {}
each do |key, _value|
hash[key] = self[key] unless hash.key?(key)
end
hash
end Возвращает новый хеш, содержащий пары заголовок-значение из self.
source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.to_h # => {"Name"=>"foo", "Value"=>"0"}
Порядок заголовков сохраняется, но повторяющиеся заголовки игнорируются:
source = "Name,Name,Name\nFoo,Bar,Baz\n"
table = CSV.parse(source, headers: true)
row = table[0]
row.to_h # => {"Name"=>"Foo"}
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.