класс CSV::Row
Строка CSV::Row — это часть Array и часть Hash. Она сохраняет порядок полей и допускает дублирование, как и Array, но также позволяет получать доступ к полям по имени, как если бы они были в Hash.
Все строки, возвращаемые CSV, будут созданы из этого класса, если обработка заголовков включена.
Атрибуты
Внутренний формат данных, используемый для сравнения на равенство.
Публичные методы класса
# File lib/csv/row.rb, line 30
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, которые должны быть массивами. Если один массив короче другого, он будет дополнен объектами nil.
Необязательный параметр header_row может быть установлен в true для указания, посредством CSV::Row.header_row?() и CSV::Row.field_row?(), что это строка заголовков. В противном случае строка предполагается строкой данных.
Объект CSV::Row поддерживает следующие методы массива Array через делегирование:
-
empty?()
-
length()
-
size()
Публичные методы экземпляра
# File lib/csv/row.rb, line 310
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 522 def ==(other) return @row == other.row if other.is_a? CSV::Row @row == other end
Возвращает true, если эта строка содержит те же заголовки и поля в том же порядке, что и other.
# File lib/csv/row.rb, line 260
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 372
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 397
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 582
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 508
def each(&block)
return enum_for(__method__) { size } unless block_given?
@row.each(&block)
self # for chaining
end Выдает каждую пару строки как кортежи заголовка и поля (подобно итерации по Hash). Этот метод возвращает строку для цепочки вызовов.
Если блок не задан, возвращается Enumerator.
Поддержка Enumerable.
# File lib/csv/row.rb, line 179
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 124
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 494 def field?(data) fields.include? data end
Возвращает true, если data соответствует полю в этой строке, и false в противном случае.
# File lib/csv/row.rb, line 69 def field_row? not header_row? end
Возвращает true, если это строка поля, false в противном случае.
# File lib/csv/row.rb, line 451
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 Возвращает значения полей в соответствии с заданными спецификаторами, которые могут быть любым сочетанием:
-
Целочисленный индекс.
-
Диапазон целочисленных индексов.
-
Массив из 2 элементов, содержащий заголовок и смещение.
-
Заголовок.
-
Диапазон заголовков.
Для спецификатора в одном из первых четырёх случаев выше, возвращает результат вычисления; см. field.
Хотя может быть любое количество спецификаторов, примеры здесь продемонстрируют по одному за раз.
Когда спецификатор — целое число значение, возвращает 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"]
Когда спецификатор — диапазон целых чисел значение, возвращает self.field(range):
row.fields(1..2) # => ["Bar", "Baz"]
Когда спецификатор — массив из 2 элементов значение, возвращает self.field(array)L
row.fields('Name', 1) # => ["Foo", "Bar"]
Когда спецификатор — заголовок значение, возвращает self.field(header)L
row.fields('Name') # => ["Foo"]
Когда спецификатор — диапазон заголовков значение, формирует новый диапазон 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 200 def has_key?(header) !!@row.assoc(header) end
Возвращает true , если существует поле с заданным заголовком, false в противном случае.
# File lib/csv/row.rb, line 61 def header_row? @header_row end
Возвращает true , если это строка заголовков, false в противном случае.
# File lib/csv/row.rb, line 81 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 483 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
Этот метод возвращает индекс поля с предоставленным заголовком. Смещение может использоваться для поиска дублирующихся имён заголовков, как описано в CSV::Row.field().
# File lib/csv/row.rb, line 51 def initialize_copy(other) super_return_value = super @row = @row.collect(&:dup) super_return_value end
# File lib/csv/row.rb, line 607
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 331
def push(*args)
args.each { |arg| self << arg }
self # for chaining
end Добавляет каждое из заданных значений к self в качестве поля; возвращает 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 561 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 542
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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.