Class: VFCSV::Row

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/vfcsv/row.rb

Overview

A CSV::Row-compatible class representing a single row of CSV data. Supports both header-based (Hash-like) and index-based (Array-like) access.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(headers, fields, header_row: false) ⇒ Row

Create a new Row

Parameters:

  • headers (Array)

    Column headers

  • fields (Array)

    Field values

  • header_row (Boolean) (defaults to: false)

    Whether this is a header row (default: false)



15
16
17
18
# File 'lib/vfcsv/row.rb', line 15

def initialize(headers, fields, header_row: false)
  @row = headers.zip(fields).map { |pair| pair }
  @header_row = header_row
end

Instance Attribute Details

#rowObject (readonly)

Returns the value of attribute row.



9
10
11
# File 'lib/vfcsv/row.rb', line 9

def row
  @row
end

Instance Method Details

#<<(arg) ⇒ self

Append a field

Parameters:

  • arg (Array)

    [header, value] pair

Returns:

  • (self)


76
77
78
79
80
81
82
83
# File 'lib/vfcsv/row.rb', line 76

def <<(arg)
  if arg.is_a?(Array) && arg.size == 2
    @row << arg.dup
  else
    @row << [nil, arg]
  end
  self
end

#==(other) ⇒ Boolean

Compare with another row

Parameters:

  • other (Row)

    Other row

Returns:

  • (Boolean)


232
233
234
235
# File 'lib/vfcsv/row.rb', line 232

def ==(other)
  return false unless other.is_a?(Row)
  @row == other.row
end

#[](header_or_index, minimum_index = 0) ⇒ String? Also known as: field

Access a field by header name or index

Parameters:

  • header_or_index (String, Integer)

    Header name or column index

  • minimum_index (Integer) (defaults to: 0)

    For duplicate headers, start searching from this index

Returns:

  • (String, nil)

    Field value



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/vfcsv/row.rb', line 37

def [](header_or_index, minimum_index = 0)
  if header_or_index.is_a?(Integer)
    pair = @row[header_or_index]
    pair&.last
  else
    # Search by header name
    @row.each_with_index do |(header, value), index|
      next if index < minimum_index
      return value if header == header_or_index
    end
    nil
  end
end

#[]=(header_or_index, value) ⇒ Object

Set a field by header name or index

Parameters:

  • header_or_index (String, Integer)

    Header name or column index

  • value (Object)

    New value



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/vfcsv/row.rb', line 55

def []=(header_or_index, value)
  if header_or_index.is_a?(Integer)
    if @row[header_or_index]
      @row[header_or_index][1] = value
    end
  else
    @row.each do |pair|
      if pair[0] == header_or_index
        pair[1] = value
        return value
      end
    end
    # Header not found, add new pair
    @row << [header_or_index, value]
  end
  value
end

#delete(header_or_index) ⇒ Array?

Delete a field by header or index

Parameters:

  • header_or_index (String, Integer)

    Header name or index

Returns:

  • (Array, nil)

    Deleted [header, value] pair



97
98
99
100
101
102
103
104
# File 'lib/vfcsv/row.rb', line 97

def delete(header_or_index)
  if header_or_index.is_a?(Integer)
    @row.delete_at(header_or_index)
  else
    index = @row.index { |pair| pair[0] == header_or_index }
    index ? @row.delete_at(index) : nil
  end
end

#delete_if {|header, value| ... } ⇒ self

Delete fields matching a condition

Yields:

  • (header, value)

    Block that returns true for fields to delete

Returns:

  • (self)


109
110
111
112
# File 'lib/vfcsv/row.rb', line 109

def delete_if(&block)
  @row.delete_if { |pair| block.call(*pair) }
  self
end

#dig(index_or_header, *identifiers) ⇒ Object

Dig into nested data

Parameters:

  • index_or_header (Integer, String)

    First key

  • identifiers (Array)

    Additional keys for nested access

Returns:

  • (Object)


198
199
200
201
202
203
204
205
206
207
208
# File 'lib/vfcsv/row.rb', line 198

def dig(index_or_header, *identifiers)
  value = self[index_or_header]
  return nil if value.nil?
  return value if identifiers.empty?

  if value.respond_to?(:dig)
    value.dig(*identifiers)
  else
    nil
  end
end

#each {|header, value| ... } ⇒ Enumerator Also known as: each_pair

Iterate over header/value pairs

Yields:

  • (header, value)

Returns:

  • (Enumerator)

    if no block given



117
118
119
120
121
# File 'lib/vfcsv/row.rb', line 117

def each(&block)
  return to_enum(__method__) unless block_given?
  @row.each { |pair| block.call(*pair) }
  self
end

#empty?Boolean

Check if row is empty

Returns:

  • (Boolean)


126
127
128
# File 'lib/vfcsv/row.rb', line 126

def empty?
  @row.empty?
end

#fetch(header_or_index, *args) { ... } ⇒ Object

Fetch a field with optional default

Parameters:

  • header_or_index (String, Integer)

    Header or index

  • default (Object)

    Default value if not found

Yields:

  • Block to call if not found

Returns:

  • (Object)

    Field value or default



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/vfcsv/row.rb', line 142

def fetch(header_or_index, *args, &block)
  value = self[header_or_index]
  return value unless value.nil?

  # Check if header actually exists with nil value
  found = if header_or_index.is_a?(Integer)
    header_or_index >= 0 && header_or_index < @row.size
  else
    @row.any? { |pair| pair[0] == header_or_index }
  end

  return value if found

  if block_given?
    block.call(header_or_index)
  elsif args.size > 0
    args[0]
  else
    raise KeyError, "key not found: #{header_or_index.inspect}"
  end
end

#field?(value) ⇒ Boolean Also known as: include?, member?

Check if a value exists in the fields

Parameters:

  • value (Object)

    Value to find

Returns:

  • (Boolean)


167
168
169
# File 'lib/vfcsv/row.rb', line 167

def field?(value)
  fields.include?(value)
end

#field_row?Boolean

Check if this is a field (data) row

Returns:

  • (Boolean)


225
226
227
# File 'lib/vfcsv/row.rb', line 225

def field_row?
  !@header_row
end

#fieldsArray Also known as: values

Get field values

Returns:

  • (Array)

    Field values



28
29
30
# File 'lib/vfcsv/row.rb', line 28

def fields
  @row.map(&:last)
end

#header?(header) ⇒ Boolean Also known as: has_key?, key?

Check if a header exists

Parameters:

  • header (String)

    Header to find

Returns:

  • (Boolean)


176
177
178
# File 'lib/vfcsv/row.rb', line 176

def header?(header)
  headers.include?(header)
end

#header_row?Boolean

Check if this is a header row

Returns:

  • (Boolean)


219
220
221
# File 'lib/vfcsv/row.rb', line 219

def header_row?
  @header_row
end

#headersArray

Get headers

Returns:

  • (Array)

    Column headers



22
23
24
# File 'lib/vfcsv/row.rb', line 22

def headers
  @row.map(&:first)
end

#index(header, minimum_index = 0) ⇒ Integer?

Find index of a header or value

Parameters:

  • header (String)

    Header to find

  • minimum_index (Integer) (defaults to: 0)

    Start index

Returns:

  • (Integer, nil)


186
187
188
189
190
191
192
# File 'lib/vfcsv/row.rb', line 186

def index(header, minimum_index = 0)
  @row.each_with_index do |(h, _v), i|
    next if i < minimum_index
    return i if h == header
  end
  nil
end

#inspectString

Inspection string

Returns:

  • (String)


281
282
283
# File 'lib/vfcsv/row.rb', line 281

def inspect
  "#<#{self.class} #{to_h.inspect}>"
end

#push(header, value) ⇒ self

Push a header and value

Parameters:

  • header (String)

    Header name

  • value (Object)

    Field value

Returns:

  • (self)


89
90
91
92
# File 'lib/vfcsv/row.rb', line 89

def push(header, value)
  @row << [header, value]
  self
end

#sizeInteger Also known as: length

Get number of fields

Returns:

  • (Integer)


132
133
134
# File 'lib/vfcsv/row.rb', line 132

def size
  @row.size
end

#to_aArray<Array>

Convert to array of [header, value] pairs

Returns:

  • (Array<Array>)


248
249
250
# File 'lib/vfcsv/row.rb', line 248

def to_a
  @row.map(&:dup)
end

#to_aryArray

Allow implicit conversion to array (for splat operations)

Returns:

  • (Array)

    Field values only



254
255
256
# File 'lib/vfcsv/row.rb', line 254

def to_ary
  fields
end

#to_csv(**options) ⇒ String Also known as: to_s

Convert to CSV string

Returns:

  • (String)


260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/vfcsv/row.rb', line 260

def to_csv(**options)
  col_sep = options[:col_sep] || ","
  quote_char = options[:quote_char] || '"'
  row_sep = options[:row_sep]
  row_sep = "\n" if row_sep.nil? || row_sep == :auto

  field_strings = fields.map do |field|
    field_str = field.to_s
    if needs_quoting?(field_str, col_sep, quote_char)
      quote_field(field_str, quote_char)
    else
      field_str
    end
  end

  field_strings.join(col_sep) + row_sep
end

#to_hHash Also known as: to_hash

Convert to hash

Returns:

  • (Hash)


239
240
241
242
243
# File 'lib/vfcsv/row.rb', line 239

def to_h
  result = {}
  @row.each { |header, value| result[header] = value }
  result
end

#values_at(*indices_or_headers) ⇒ Array

Get values at specified indices or headers

Parameters:

  • indices_or_headers (Array)

    List of indices or headers

Returns:

  • (Array)

    Values at specified positions



213
214
215
# File 'lib/vfcsv/row.rb', line 213

def values_at(*indices_or_headers)
  indices_or_headers.map { |i| self[i] }
end