# class CSV::Row

A `CSV::Row` is part `Array` and part `Hash`. It retains an order for the fields and allows duplicates just as an `Array` would, but also allows you to access fields by name just as you could if they were in a `Hash`.

All rows returned by `CSV` will be constructed from this class, if header row processing is activated.

### Attributes

row[R]

Internal data format used to compare equality.

### Public Class Methods

new(headers, fields, header_row = false) click to toggle source

Constructs a new `CSV::Row` from `headers` and `fields`, which are expected to be Arrays. If one `Array` is shorter than the other, it will be padded with `nil` objects.

The optional `header_row` parameter can be set to `true` to indicate, via `CSV::Row.header_row?()` and `CSV::Row.field_row?()`, that this is a header row. Otherwise, the row assumes to be a field row.

A `CSV::Row` object supports the following `Array` methods through delegation:

- empty?()

- length()

- size()

    # 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

### Public Instance Methods

<<( field ) click to toggle source
<<( header_and_field_array )
<<( header_and_field_hash )

If a two-element `Array` is provided, it is assumed to be a header and field and the pair is appended. A `Hash` works the same way with the key being the header and the value being the field. Anything else is assumed to be a lone field which is appended with a `nil` header.

This method returns the row for chaining.

    # File lib/csv/row.rb, line 181
    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

==(other) click to toggle source

Returns `true` if this row contains the same headers and fields in the same order as `other`.

    # File lib/csv/row.rb, line 322
    def ==(other)
      return @row == other.row if other.is_a? CSV::Row
      @row == other
    end

[](header_or_index, minimum_index = 0)
Alias for: field
[]=( header, value ) click to toggle source
[]=( header, offset, value )
[]=( index, value )

Looks up the field by the semantics described in `CSV::Row.field()` and assigns the `value`.

Assigning past the end of the row with an index will set all pairs between to `[nil, nil]`. Assigning to an unused header appends the new pair.

    # File lib/csv/row.rb, line 148
    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

delete( header ) click to toggle source
delete( header, offset )
delete( index )

Removes a pair from the row by `header` or `index`. The pair is located as described in `CSV::Row.field()`. The deleted pair is returned, or `nil` if a pair could not be found.

    # File lib/csv/row.rb, line 216
    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

delete_if(&block) click to toggle source

The provided `block` is passed a header and field for each pair in the row and expected to return `true` or `false`, depending on whether the pair should be deleted.

This method returns the row for chaining.

If no block is given, an `Enumerator` is returned.

    # File lib/csv/row.rb, line 235
    def delete_if(&block)
      return enum_for(__method__) { size } unless block_given?

      @row.delete_if(&block)

      self  # for chaining
    end

dig(index_or_header, *indexes) click to toggle source

Extracts the nested value specified by the sequence of `index` or `header` objects by calling dig at each step, returning nil if any intermediate step is nil.

    # File lib/csv/row.rb, line 356
    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

each(&block) click to toggle source

Yields each pair of the row as header and field tuples (much like iterating over a `Hash`). This method returns the row for chaining.

If no block is given, an `Enumerator` is returned.

Support for `Enumerable`.

    # File lib/csv/row.rb, line 308
    def each(&block)
      return enum_for(__method__) { size } unless block_given?

      @row.each(&block)

      self  # for chaining
    end

Also aliased as: each_pair
each_pair(&block)
Alias for: each
fetch( header ) click to toggle source
fetch( header ) { |row| ... }
fetch( header, default )

This method will fetch the field value by `header`. It has the same behavior as `Hash#fetch`: if there is a field with the given `header`, its value is returned. Otherwise, if a block is given, it is yielded the `header` and its result is returned; if a `default` is given as the second argument, it is returned; otherwise a `KeyError` is raised.

    # File lib/csv/row.rb, line 110
    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

field( header ) click to toggle source
field( header, offset )
field( index )

This method will return the field value by `header` or `index`. If a field is not found, `nil` is returned.

When provided, `offset` ensures that a header match occurs on or later than the `offset` index. You can use this to find duplicate headers, without resorting to hard-coding exact indices.

    # File lib/csv/row.rb, line 84
    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].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

Also aliased as: []
field?(data) click to toggle source

Returns `true` if `data` matches a field in this row, and `false` otherwise.

    # File lib/csv/row.rb, line 294
    def field?(data)
      fields.include? data
    end

field_row?() click to toggle source

Returns `true` if this is a field row.

    # File lib/csv/row.rb, line 62
    def field_row?
      not header_row?
    end

fields(*headers_and_or_indices) click to toggle source

This method accepts any number of arguments which can be headers, indices, Ranges of either, or two-element Arrays containing a header and offset. Each argument will be replaced with a field lookup as described in `CSV::Row.field()`.

If called with no arguments, all fields are returned.

    # File lib/csv/row.rb, line 251
    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

Also aliased as: values_at
has_key?(header) click to toggle source

Returns `true` if there is a field with the given `header`.

    # File lib/csv/row.rb, line 127
    def has_key?(header)
      !!@row.assoc(header)
    end

Also aliased as: include?, key?, member?, header?
header?(header)
Alias for: has_key?
header_row?() click to toggle source

Returns `true` if this is a header row.

    # File lib/csv/row.rb, line 57
    def header_row?
      @header_row
    end

headers() click to toggle source

Returns the headers of this row.

    # File lib/csv/row.rb, line 67
    def headers
      @row.map(&:first)
    end

include?(header)
Alias for: has_key?
index( header ) click to toggle source
index( header, offset )

This method will return the index of a field with the provided `header`. The `offset` can be used to locate duplicate header names, as described in `CSV::Row.field()`.

    # File lib/csv/row.rb, line 283
    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

initialize_copy(other) click to toggle source
Calls superclass method

    # File lib/csv/row.rb, line 51
    def initialize_copy(other)
      super
      @row = @row.dup
    end

inspect() click to toggle source

A summary of fields, by header, in an ASCII compatible `String`.

    # File lib/csv/row.rb, line 373
    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

key?(header)
Alias for: has_key?
member?(header)
Alias for: has_key?
push(*args) click to toggle source

A shortcut for appending multiple fields. Equivalent to:

    args.each { |arg| csv_row << arg }

This method returns the row for chaining.

    # File lib/csv/row.rb, line 200
    def push(*args)
      args.each { |arg| self << arg }

      self  # for chaining
    end

to_csv(**options) click to toggle source

Returns the row as a `CSV` `String`. Headers are not used. Equivalent to:

    csv_row.fields.to_csv( options )

    # File lib/csv/row.rb, line 347
    def to_csv(**options)
      fields.to_csv(**options)
    end

Also aliased as: to_s
to_h() click to toggle source

Collapses the row into a simple `Hash`. Be warned that this discards field order and clobbers duplicate fields.

    # File lib/csv/row.rb, line 331
    def to_h
      hash = {}
      each do |key, _value|
        hash[key] = self[key] unless hash.key?(key)
      end
      hash
    end

Also aliased as: to_hash
to_hash()
Alias for: to_h
to_s(**options)
Alias for: to_csv
values_at(*headers_and_or_indices)
Alias for: fields