csv 3.1.7 → 3.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +11 -0
- data/README.md +5 -0
- data/doc/csv/options/common/col_sep.rdoc +1 -7
- data/doc/csv/options/common/row_sep.rdoc +0 -9
- data/doc/csv/options/generating/write_converters.rdoc +0 -8
- data/doc/csv/recipes/filtering.rdoc +158 -0
- data/doc/csv/recipes/generating.rdoc +298 -0
- data/doc/csv/recipes/parsing.rdoc +545 -0
- data/doc/csv/recipes/recipes.rdoc +6 -0
- data/lib/csv.rb +67 -26
- data/lib/csv/row.rb +477 -132
- data/lib/csv/table.rb +486 -65
- data/lib/csv/version.rb +1 -1
- metadata +11 -3
data/lib/csv/table.rb
CHANGED
@@ -4,12 +4,12 @@ require "forwardable"
|
|
4
4
|
|
5
5
|
class CSV
|
6
6
|
# = \CSV::Table
|
7
|
-
# A \CSV::Table instance
|
7
|
+
# A \CSV::Table instance represents \CSV data.
|
8
8
|
# (see {class CSV}[../CSV.html]).
|
9
9
|
#
|
10
10
|
# The instance may have:
|
11
|
-
# - Rows:
|
12
|
-
# - Headers:
|
11
|
+
# - Rows: each is a Table::Row object.
|
12
|
+
# - Headers: names for the columns.
|
13
13
|
#
|
14
14
|
# === Instance Methods
|
15
15
|
#
|
@@ -143,7 +143,7 @@ class CSV
|
|
143
143
|
# table['Name'] # => ["Foo", "Bar", "Baz"]
|
144
144
|
class Table
|
145
145
|
# :call-seq:
|
146
|
-
# CSV::Table.new(array_of_rows, headers = nil)
|
146
|
+
# CSV::Table.new(array_of_rows, headers = nil) -> csv_table
|
147
147
|
#
|
148
148
|
# Returns a new \CSV::Table object.
|
149
149
|
#
|
@@ -223,7 +223,7 @@ class CSV
|
|
223
223
|
def_delegators :@table, :empty?, :length, :size
|
224
224
|
|
225
225
|
# :call-seq:
|
226
|
-
# table.by_col
|
226
|
+
# table.by_col -> table_dup
|
227
227
|
#
|
228
228
|
# Returns a duplicate of +self+, in column mode
|
229
229
|
# (see {Column Mode}[#class-CSV::Table-label-Column+Mode]):
|
@@ -244,7 +244,7 @@ class CSV
|
|
244
244
|
end
|
245
245
|
|
246
246
|
# :call-seq:
|
247
|
-
# table.by_col!
|
247
|
+
# table.by_col! -> self
|
248
248
|
#
|
249
249
|
# Sets the mode for +self+ to column mode
|
250
250
|
# (see {Column Mode}[#class-CSV::Table-label-Column+Mode]); returns +self+:
|
@@ -261,7 +261,7 @@ class CSV
|
|
261
261
|
end
|
262
262
|
|
263
263
|
# :call-seq:
|
264
|
-
# table.by_col_or_row
|
264
|
+
# table.by_col_or_row -> table_dup
|
265
265
|
#
|
266
266
|
# Returns a duplicate of +self+, in mixed mode
|
267
267
|
# (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]):
|
@@ -282,7 +282,7 @@ class CSV
|
|
282
282
|
end
|
283
283
|
|
284
284
|
# :call-seq:
|
285
|
-
# table.by_col_or_row!
|
285
|
+
# table.by_col_or_row! -> self
|
286
286
|
#
|
287
287
|
# Sets the mode for +self+ to mixed mode
|
288
288
|
# (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]); returns +self+:
|
@@ -299,7 +299,7 @@ class CSV
|
|
299
299
|
end
|
300
300
|
|
301
301
|
# :call-seq:
|
302
|
-
# table.by_row
|
302
|
+
# table.by_row -> table_dup
|
303
303
|
#
|
304
304
|
# Returns a duplicate of +self+, in row mode
|
305
305
|
# (see {Row Mode}[#class-CSV::Table-label-Row+Mode]):
|
@@ -320,7 +320,7 @@ class CSV
|
|
320
320
|
end
|
321
321
|
|
322
322
|
# :call-seq:
|
323
|
-
# table.by_row!
|
323
|
+
# table.by_row! -> self
|
324
324
|
#
|
325
325
|
# Sets the mode for +self+ to row mode
|
326
326
|
# (see {Row Mode}[#class-CSV::Table-label-Row+Mode]); returns +self+:
|
@@ -337,7 +337,7 @@ class CSV
|
|
337
337
|
end
|
338
338
|
|
339
339
|
# :call-seq:
|
340
|
-
# table.headers
|
340
|
+
# table.headers -> array_of_headers
|
341
341
|
#
|
342
342
|
# Returns a new \Array containing the \String headers for the table.
|
343
343
|
#
|
@@ -365,14 +365,152 @@ class CSV
|
|
365
365
|
end
|
366
366
|
end
|
367
367
|
|
368
|
+
# :call-seq:
|
369
|
+
# table[n] -> row or column_data
|
370
|
+
# table[range] -> array_of_rows or array_of_column_data
|
371
|
+
# table[header] -> array_of_column_data
|
372
|
+
#
|
373
|
+
# Returns data from the table; does not modify the table.
|
374
|
+
#
|
375
|
+
# ---
|
376
|
+
#
|
377
|
+
# Fetch a \Row by Its \Integer Index::
|
378
|
+
# - Form: <tt>table[n]</tt>, +n+ an integer.
|
379
|
+
# - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
|
380
|
+
# - Return value: _nth_ row of the table, if that row exists;
|
381
|
+
# otherwise +nil+.
|
382
|
+
#
|
383
|
+
# Returns the _nth_ row of the table if that row exists:
|
384
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
385
|
+
# table = CSV.parse(source, headers: true)
|
386
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
387
|
+
# table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
|
388
|
+
# table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
|
389
|
+
# table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
|
390
|
+
#
|
391
|
+
# Counts backward from the last row if +n+ is negative:
|
392
|
+
# table[-1] # => #<CSV::Row "Name":"baz" "Value":"2">
|
393
|
+
#
|
394
|
+
# Returns +nil+ if +n+ is too large or too small:
|
395
|
+
# table[4] # => nil
|
396
|
+
# table[-4] # => nil
|
397
|
+
#
|
398
|
+
# Raises an exception if the access mode is <tt>:row</tt>
|
399
|
+
# and +n+ is not an \Integer:
|
400
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
401
|
+
# # Raises TypeError (no implicit conversion of String into Integer):
|
402
|
+
# table['Name']
|
403
|
+
#
|
404
|
+
# ---
|
405
|
+
#
|
406
|
+
# Fetch a Column by Its \Integer Index::
|
407
|
+
# - Form: <tt>table[n]</tt>, +n+ an \Integer.
|
408
|
+
# - Access mode: <tt>:col</tt>.
|
409
|
+
# - Return value: _nth_ column of the table, if that column exists;
|
410
|
+
# otherwise an \Array of +nil+ fields of length <tt>self.size</tt>.
|
411
|
+
#
|
412
|
+
# Returns the _nth_ column of the table if that column exists:
|
413
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
414
|
+
# table = CSV.parse(source, headers: true)
|
415
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
416
|
+
# table[1] # => ["0", "1", "2"]
|
417
|
+
#
|
418
|
+
# Counts backward from the last column if +n+ is negative:
|
419
|
+
# table[-2] # => ["foo", "bar", "baz"]
|
420
|
+
#
|
421
|
+
# Returns an \Array of +nil+ fields if +n+ is too large or too small:
|
422
|
+
# table[4] # => [nil, nil, nil]
|
423
|
+
# table[-4] # => [nil, nil, nil]
|
424
|
+
#
|
425
|
+
# ---
|
426
|
+
#
|
427
|
+
# Fetch Rows by \Range::
|
428
|
+
# - Form: <tt>table[range]</tt>, +range+ a \Range object.
|
429
|
+
# - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
|
430
|
+
# - Return value: rows from the table, beginning at row <tt>range.start</tt>,
|
431
|
+
# if those rows exists.
|
432
|
+
#
|
433
|
+
# Returns rows from the table, beginning at row <tt>range.first</tt>,
|
434
|
+
# if those rows exist:
|
435
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
436
|
+
# table = CSV.parse(source, headers: true)
|
437
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
438
|
+
# rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
|
439
|
+
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
|
440
|
+
# table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
|
441
|
+
# rows = table[1..2] # => #<CSV::Row "Name":"bar" "Value":"1">
|
442
|
+
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
|
443
|
+
#
|
444
|
+
# If there are too few rows, returns all from <tt>range.start</tt> to the end:
|
445
|
+
# rows = table[1..50] # => #<CSV::Row "Name":"bar" "Value":"1">
|
446
|
+
# rows # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
|
447
|
+
#
|
448
|
+
# Special case: if <tt>range.start == table.size</tt>, returns an empty \Array:
|
449
|
+
# table[table.size..50] # => []
|
450
|
+
#
|
451
|
+
# If <tt>range.end</tt> is negative, calculates the ending index from the end:
|
452
|
+
# rows = table[0..-1]
|
453
|
+
# rows # => [#<CSV::Row "Name":"foo" "Value":"0">, #<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
|
454
|
+
#
|
455
|
+
# If <tt>range.start</tt> is negative, calculates the starting index from the end:
|
456
|
+
# rows = table[-1..2]
|
457
|
+
# rows # => [#<CSV::Row "Name":"baz" "Value":"2">]
|
458
|
+
#
|
459
|
+
# If <tt>range.start</tt> is larger than <tt>table.size</tt>, returns +nil+:
|
460
|
+
# table[4..4] # => nil
|
461
|
+
#
|
462
|
+
# ---
|
463
|
+
#
|
464
|
+
# Fetch Columns by \Range::
|
465
|
+
# - Form: <tt>table[range]</tt>, +range+ a \Range object.
|
466
|
+
# - Access mode: <tt>:col</tt>.
|
467
|
+
# - Return value: column data from the table, beginning at column <tt>range.start</tt>,
|
468
|
+
# if those columns exist.
|
469
|
+
#
|
470
|
+
# Returns column values from the table, if the column exists;
|
471
|
+
# the values are arranged by row:
|
472
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
473
|
+
# table = CSV.parse(source, headers: true)
|
474
|
+
# table.by_col!
|
475
|
+
# table[0..1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
476
|
+
#
|
477
|
+
# Special case: if <tt>range.start == headers.size</tt>,
|
478
|
+
# returns an \Array (size: <tt>table.size</tt>) of empty \Arrays:
|
479
|
+
# table[table.headers.size..50] # => [[], [], []]
|
480
|
+
#
|
481
|
+
# If <tt>range.end</tt> is negative, calculates the ending index from the end:
|
482
|
+
# table[0..-1] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
483
|
+
#
|
484
|
+
# If <tt>range.start</tt> is negative, calculates the starting index from the end:
|
485
|
+
# table[-2..2] # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
368
486
|
#
|
369
|
-
#
|
370
|
-
#
|
371
|
-
#
|
487
|
+
# If <tt>range.start</tt> is larger than <tt>table.size</tt>,
|
488
|
+
# returns an \Array of +nil+ values:
|
489
|
+
# table[4..4] # => [nil, nil, nil]
|
372
490
|
#
|
373
|
-
#
|
374
|
-
#
|
491
|
+
# ---
|
492
|
+
#
|
493
|
+
# Fetch a Column by Its \String Header::
|
494
|
+
# - Form: <tt>table[header]</tt>, +header+ a \String header.
|
495
|
+
# - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>
|
496
|
+
# - Return value: column data from the table, if that +header+ exists.
|
497
|
+
#
|
498
|
+
# Returns column values from the table, if the column exists:
|
499
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
500
|
+
# table = CSV.parse(source, headers: true)
|
501
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
502
|
+
# table['Name'] # => ["foo", "bar", "baz"]
|
503
|
+
# table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
|
504
|
+
# col = table['Name']
|
505
|
+
# col # => ["foo", "bar", "baz"]
|
375
506
|
#
|
507
|
+
# Modifying the returned column values does not modify the table:
|
508
|
+
# col[0] = 'bat'
|
509
|
+
# col # => ["bat", "bar", "baz"]
|
510
|
+
# table['Name'] # => ["foo", "bar", "baz"]
|
511
|
+
#
|
512
|
+
# Returns an \Array of +nil+ values if there is no such column:
|
513
|
+
# table['Nosuch'] # => [nil, nil, nil]
|
376
514
|
def [](index_or_header)
|
377
515
|
if @mode == :row or # by index
|
378
516
|
(@mode == :col_or_row and (index_or_header.is_a?(Integer) or index_or_header.is_a?(Range)))
|
@@ -382,22 +520,132 @@ class CSV
|
|
382
520
|
end
|
383
521
|
end
|
384
522
|
|
523
|
+
# :call-seq:
|
524
|
+
# table[n] = row -> row
|
525
|
+
# table[n] = field_or_array_of_fields -> field_or_array_of_fields
|
526
|
+
# table[header] = field_or_array_of_fields -> field_or_array_of_fields
|
527
|
+
#
|
528
|
+
# Puts data onto the table.
|
529
|
+
#
|
530
|
+
# ---
|
531
|
+
#
|
532
|
+
# Set a \Row by Its \Integer Index::
|
533
|
+
# - Form: <tt>table[n] = row</tt>, +n+ an \Integer,
|
534
|
+
# +row+ a \CSV::Row instance or an \Array of fields.
|
535
|
+
# - Access mode: <tt>:row</tt> or <tt>:col_or_row</tt>.
|
536
|
+
# - Return value: +row+.
|
537
|
+
#
|
538
|
+
# If the row exists, it is replaced:
|
539
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
540
|
+
# table = CSV.parse(source, headers: true)
|
541
|
+
# new_row = CSV::Row.new(['Name', 'Value'], ['bat', 3])
|
542
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
543
|
+
# return_value = table[0] = new_row
|
544
|
+
# return_value.equal?(new_row) # => true # Returned the row
|
545
|
+
# table[0].to_h # => {"Name"=>"bat", "Value"=>3}
|
385
546
|
#
|
386
|
-
#
|
387
|
-
#
|
388
|
-
#
|
547
|
+
# With access mode <tt>:col_or_row</tt>:
|
548
|
+
# table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
|
549
|
+
# table[0] = CSV::Row.new(['Name', 'Value'], ['bam', 4])
|
550
|
+
# table[0].to_h # => {"Name"=>"bam", "Value"=>4}
|
389
551
|
#
|
390
|
-
#
|
391
|
-
#
|
552
|
+
# With an \Array instead of a \CSV::Row, inherits headers from the table:
|
553
|
+
# array = ['bad', 5]
|
554
|
+
# return_value = table[0] = array
|
555
|
+
# return_value.equal?(array) # => true # Returned the array
|
556
|
+
# table[0].to_h # => {"Name"=>"bad", "Value"=>5}
|
392
557
|
#
|
393
|
-
#
|
394
|
-
#
|
395
|
-
#
|
396
|
-
#
|
558
|
+
# If the row does not exist, extends the table by adding rows:
|
559
|
+
# assigns rows with +nil+ as needed:
|
560
|
+
# table.size # => 3
|
561
|
+
# table[5] = ['bag', 6]
|
562
|
+
# table.size # => 6
|
563
|
+
# table[3] # => nil
|
564
|
+
# table[4]# => nil
|
565
|
+
# table[5].to_h # => {"Name"=>"bag", "Value"=>6}
|
397
566
|
#
|
398
|
-
#
|
399
|
-
# new columns creates them at the right end of the table.
|
567
|
+
# Note that the +nil+ rows are actually +nil+, not a row of +nil+ fields.
|
400
568
|
#
|
569
|
+
# ---
|
570
|
+
#
|
571
|
+
# Set a Column by Its \Integer Index::
|
572
|
+
# - Form: <tt>table[n] = array_of_fields</tt>, +n+ an \Integer,
|
573
|
+
# +array_of_fields+ an \Array of \String fields.
|
574
|
+
# - Access mode: <tt>:col</tt>.
|
575
|
+
# - Return value: +array_of_fields+.
|
576
|
+
#
|
577
|
+
# If the column exists, it is replaced:
|
578
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
579
|
+
# table = CSV.parse(source, headers: true)
|
580
|
+
# new_col = [3, 4, 5]
|
581
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
582
|
+
# return_value = table[1] = new_col
|
583
|
+
# return_value.equal?(new_col) # => true # Returned the column
|
584
|
+
# table[1] # => [3, 4, 5]
|
585
|
+
# # The rows, as revised:
|
586
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
587
|
+
# table[0].to_h # => {"Name"=>"foo", "Value"=>3}
|
588
|
+
# table[1].to_h # => {"Name"=>"bar", "Value"=>4}
|
589
|
+
# table[2].to_h # => {"Name"=>"baz", "Value"=>5}
|
590
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
591
|
+
#
|
592
|
+
# If there are too few values, fills with +nil+ values:
|
593
|
+
# table[1] = [0]
|
594
|
+
# table[1] # => [0, nil, nil]
|
595
|
+
#
|
596
|
+
# If there are too many values, ignores the extra values:
|
597
|
+
# table[1] = [0, 1, 2, 3, 4]
|
598
|
+
# table[1] # => [0, 1, 2]
|
599
|
+
#
|
600
|
+
# If a single value is given, replaces all fields in the column with that value:
|
601
|
+
# table[1] = 'bat'
|
602
|
+
# table[1] # => ["bat", "bat", "bat"]
|
603
|
+
#
|
604
|
+
# ---
|
605
|
+
#
|
606
|
+
# Set a Column by Its \String Header::
|
607
|
+
# - Form: <tt>table[header] = field_or_array_of_fields</tt>,
|
608
|
+
# +header+ a \String header, +field_or_array_of_fields+ a field value
|
609
|
+
# or an \Array of \String fields.
|
610
|
+
# - Access mode: <tt>:col</tt> or <tt>:col_or_row</tt>.
|
611
|
+
# - Return value: +field_or_array_of_fields+.
|
612
|
+
#
|
613
|
+
# If the column exists, it is replaced:
|
614
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
615
|
+
# table = CSV.parse(source, headers: true)
|
616
|
+
# new_col = [3, 4, 5]
|
617
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
618
|
+
# return_value = table['Value'] = new_col
|
619
|
+
# return_value.equal?(new_col) # => true # Returned the column
|
620
|
+
# table['Value'] # => [3, 4, 5]
|
621
|
+
# # The rows, as revised:
|
622
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
623
|
+
# table[0].to_h # => {"Name"=>"foo", "Value"=>3}
|
624
|
+
# table[1].to_h # => {"Name"=>"bar", "Value"=>4}
|
625
|
+
# table[2].to_h # => {"Name"=>"baz", "Value"=>5}
|
626
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
627
|
+
#
|
628
|
+
# If there are too few values, fills with +nil+ values:
|
629
|
+
# table['Value'] = [0]
|
630
|
+
# table['Value'] # => [0, nil, nil]
|
631
|
+
#
|
632
|
+
# If there are too many values, ignores the extra values:
|
633
|
+
# table['Value'] = [0, 1, 2, 3, 4]
|
634
|
+
# table['Value'] # => [0, 1, 2]
|
635
|
+
#
|
636
|
+
# If the column does not exist, extends the table by adding columns:
|
637
|
+
# table['Note'] = ['x', 'y', 'z']
|
638
|
+
# table['Note'] # => ["x", "y", "z"]
|
639
|
+
# # The rows, as revised:
|
640
|
+
# table.by_row!
|
641
|
+
# table[0].to_h # => {"Name"=>"foo", "Value"=>0, "Note"=>"x"}
|
642
|
+
# table[1].to_h # => {"Name"=>"bar", "Value"=>1, "Note"=>"y"}
|
643
|
+
# table[2].to_h # => {"Name"=>"baz", "Value"=>2, "Note"=>"z"}
|
644
|
+
# table.by_col!
|
645
|
+
#
|
646
|
+
# If a single value is given, replaces all fields in the column with that value:
|
647
|
+
# table['Value'] = 'bat'
|
648
|
+
# table['Value'] # => ["bat", "bat", "bat"]
|
401
649
|
def []=(index_or_header, value)
|
402
650
|
if @mode == :row or # by index
|
403
651
|
(@mode == :col_or_row and index_or_header.is_a? Integer)
|
@@ -431,15 +679,58 @@ class CSV
|
|
431
679
|
end
|
432
680
|
end
|
433
681
|
|
682
|
+
# :call-seq:
|
683
|
+
# table.values_at(*indexes) -> array_of_rows
|
684
|
+
# table.values_at(*headers) -> array_of_columns_data
|
685
|
+
#
|
686
|
+
# If the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
|
687
|
+
# and each argument is either an \Integer or a \Range,
|
688
|
+
# returns rows.
|
689
|
+
# Otherwise, returns columns data.
|
690
|
+
#
|
691
|
+
# In either case, the returned values are in the order
|
692
|
+
# specified by the arguments. Arguments may be repeated.
|
434
693
|
#
|
435
|
-
#
|
436
|
-
#
|
437
|
-
#
|
438
|
-
#
|
439
|
-
#
|
694
|
+
# ---
|
695
|
+
#
|
696
|
+
# Returns rows as an \Array of \CSV::Row objects.
|
697
|
+
#
|
698
|
+
# No argument:
|
699
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
700
|
+
# table = CSV.parse(source, headers: true)
|
701
|
+
# table.values_at # => []
|
440
702
|
#
|
441
|
-
#
|
703
|
+
# One index:
|
704
|
+
# values = table.values_at(0)
|
705
|
+
# values # => [#<CSV::Row "Name":"foo" "Value":"0">]
|
442
706
|
#
|
707
|
+
# Two indexes:
|
708
|
+
# values = table.values_at(2, 0)
|
709
|
+
# values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
|
710
|
+
#
|
711
|
+
# One \Range:
|
712
|
+
# values = table.values_at(1..2)
|
713
|
+
# values # => [#<CSV::Row "Name":"bar" "Value":"1">, #<CSV::Row "Name":"baz" "Value":"2">]
|
714
|
+
#
|
715
|
+
# \Ranges and indexes:
|
716
|
+
# values = table.values_at(0..1, 1..2, 0, 2)
|
717
|
+
# pp values
|
718
|
+
# Output:
|
719
|
+
# [#<CSV::Row "Name":"foo" "Value":"0">,
|
720
|
+
# #<CSV::Row "Name":"bar" "Value":"1">,
|
721
|
+
# #<CSV::Row "Name":"bar" "Value":"1">,
|
722
|
+
# #<CSV::Row "Name":"baz" "Value":"2">,
|
723
|
+
# #<CSV::Row "Name":"foo" "Value":"0">,
|
724
|
+
# #<CSV::Row "Name":"baz" "Value":"2">]
|
725
|
+
#
|
726
|
+
# ---
|
727
|
+
#
|
728
|
+
# Returns columns data as row Arrays,
|
729
|
+
# each consisting of the specified columns data for that row:
|
730
|
+
# values = table.values_at('Name')
|
731
|
+
# values # => [["foo"], ["bar"], ["baz"]]
|
732
|
+
# values = table.values_at('Value', 'Name')
|
733
|
+
# values # => [["0", "foo"], ["1", "bar"], ["2", "baz"]]
|
443
734
|
def values_at(*indices_or_headers)
|
444
735
|
if @mode == :row or # by indices
|
445
736
|
( @mode == :col_or_row and indices_or_headers.all? do |index|
|
@@ -454,13 +745,20 @@ class CSV
|
|
454
745
|
end
|
455
746
|
end
|
456
747
|
|
748
|
+
# :call-seq:
|
749
|
+
# table << row_or_array -> self
|
457
750
|
#
|
458
|
-
#
|
459
|
-
#
|
460
|
-
#
|
461
|
-
#
|
462
|
-
#
|
751
|
+
# If +row_or_array+ is a \CSV::Row object,
|
752
|
+
# it is appended to the table:
|
753
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
754
|
+
# table = CSV.parse(source, headers: true)
|
755
|
+
# table << CSV::Row.new(table.headers, ['bat', 3])
|
756
|
+
# table[3] # => #<CSV::Row "Name":"bat" "Value":3>
|
463
757
|
#
|
758
|
+
# If +row_or_array+ is an \Array, it is used to create a new
|
759
|
+
# \CSV::Row object which is then appended to the table:
|
760
|
+
# table << ['bam', 4]
|
761
|
+
# table[4] # => #<CSV::Row "Name":"bam" "Value":4>
|
464
762
|
def <<(row_or_array)
|
465
763
|
if row_or_array.is_a? Array # append Array
|
466
764
|
@table << Row.new(headers, row_or_array)
|
@@ -472,23 +770,67 @@ class CSV
|
|
472
770
|
end
|
473
771
|
|
474
772
|
#
|
475
|
-
#
|
476
|
-
#
|
477
|
-
# rows.each { |row| self << row }
|
773
|
+
# :call-seq:
|
774
|
+
# table.push(*rows_or_arrays) -> self
|
478
775
|
#
|
479
|
-
#
|
776
|
+
# A shortcut for appending multiple rows. Equivalent to:
|
777
|
+
# rows.each {|row| self << row }
|
480
778
|
#
|
779
|
+
# Each argument may be either a \CSV::Row object or an \Array:
|
780
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
781
|
+
# table = CSV.parse(source, headers: true)
|
782
|
+
# rows = [
|
783
|
+
# CSV::Row.new(table.headers, ['bat', 3]),
|
784
|
+
# ['bam', 4]
|
785
|
+
# ]
|
786
|
+
# table.push(*rows)
|
787
|
+
# table[3..4] # => [#<CSV::Row "Name":"bat" "Value":3>, #<CSV::Row "Name":"bam" "Value":4>]
|
481
788
|
def push(*rows)
|
482
789
|
rows.each { |row| self << row }
|
483
790
|
|
484
791
|
self # for chaining
|
485
792
|
end
|
486
793
|
|
794
|
+
# :call-seq:
|
795
|
+
# table.delete(*indexes) -> deleted_values
|
796
|
+
# table.delete(*headers) -> deleted_values
|
487
797
|
#
|
488
|
-
#
|
489
|
-
#
|
490
|
-
#
|
798
|
+
# If the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
|
799
|
+
# and each argument is either an \Integer or a \Range,
|
800
|
+
# returns deleted rows.
|
801
|
+
# Otherwise, returns deleted columns data.
|
802
|
+
#
|
803
|
+
# In either case, the returned values are in the order
|
804
|
+
# specified by the arguments. Arguments may be repeated.
|
805
|
+
#
|
806
|
+
# ---
|
807
|
+
#
|
808
|
+
# Returns rows as an \Array of \CSV::Row objects.
|
809
|
+
#
|
810
|
+
# One index:
|
811
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
812
|
+
# table = CSV.parse(source, headers: true)
|
813
|
+
# deleted_values = table.delete(0)
|
814
|
+
# deleted_values # => [#<CSV::Row "Name":"foo" "Value":"0">]
|
815
|
+
#
|
816
|
+
# Two indexes:
|
817
|
+
# table = CSV.parse(source, headers: true)
|
818
|
+
# deleted_values = table.delete(2, 0)
|
819
|
+
# deleted_values # => [#<CSV::Row "Name":"baz" "Value":"2">, #<CSV::Row "Name":"foo" "Value":"0">]
|
491
820
|
#
|
821
|
+
# ---
|
822
|
+
#
|
823
|
+
# Returns columns data as column Arrays.
|
824
|
+
#
|
825
|
+
# One header:
|
826
|
+
# table = CSV.parse(source, headers: true)
|
827
|
+
# deleted_values = table.delete('Name')
|
828
|
+
# deleted_values # => ["foo", "bar", "baz"]
|
829
|
+
#
|
830
|
+
# Two headers:
|
831
|
+
# table = CSV.parse(source, headers: true)
|
832
|
+
# deleted_values = table.delete('Value', 'Name')
|
833
|
+
# deleted_values # => [["0", "1", "2"], ["foo", "bar", "baz"]]
|
492
834
|
def delete(*indexes_or_headers)
|
493
835
|
if indexes_or_headers.empty?
|
494
836
|
raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
|
@@ -513,16 +855,35 @@ class CSV
|
|
513
855
|
end
|
514
856
|
end
|
515
857
|
|
858
|
+
# :call-seq:
|
859
|
+
# table.delete_if {|row_or_column| ... } -> self
|
516
860
|
#
|
517
|
-
# Removes
|
518
|
-
#
|
519
|
-
# walking of rows. In column mode, iteration will +yield+ two element
|
520
|
-
# tuples containing the column name and an Array of values for that column.
|
861
|
+
# Removes rows or columns for which the block returns a truthy value;
|
862
|
+
# returns +self+.
|
521
863
|
#
|
522
|
-
#
|
864
|
+
# Removes rows when the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>;
|
865
|
+
# calls the block with each \CSV::Row object:
|
866
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
867
|
+
# table = CSV.parse(source, headers: true)
|
868
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
869
|
+
# table.size # => 3
|
870
|
+
# table.delete_if {|row| row['Name'].start_with?('b') }
|
871
|
+
# table.size # => 1
|
523
872
|
#
|
524
|
-
#
|
873
|
+
# Removes columns when the access mode is <tt>:col</tt>;
|
874
|
+
# calls the block with each column as a 2-element array
|
875
|
+
# containing the header and an \Array of column fields:
|
876
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
877
|
+
# table = CSV.parse(source, headers: true)
|
878
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
879
|
+
# table.headers.size # => 2
|
880
|
+
# table.delete_if {|column_data| column_data[1].include?('2') }
|
881
|
+
# table.headers.size # => 1
|
525
882
|
#
|
883
|
+
# Returns a new \Enumerator if no block is given:
|
884
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
885
|
+
# table = CSV.parse(source, headers: true)
|
886
|
+
# table.delete_if # => #<Enumerator: #<CSV::Table mode:col_or_row row_count:4>:delete_if>
|
526
887
|
def delete_if(&block)
|
527
888
|
return enum_for(__method__) { @mode == :row or @mode == :col_or_row ? size : headers.size } unless block_given?
|
528
889
|
|
@@ -540,15 +901,33 @@ class CSV
|
|
540
901
|
|
541
902
|
include Enumerable
|
542
903
|
|
904
|
+
# :call-seq:
|
905
|
+
# table.each {|row_or_column| ... ) -> self
|
543
906
|
#
|
544
|
-
#
|
545
|
-
# walking of rows. In column mode, iteration will +yield+ two element
|
546
|
-
# tuples containing the column name and an Array of values for that column.
|
907
|
+
# Calls the block with each row or column; returns +self+.
|
547
908
|
#
|
548
|
-
#
|
909
|
+
# When the access mode is <tt>:row</tt> or <tt>:col_or_row</tt>,
|
910
|
+
# calls the block with each \CSV::Row object:
|
911
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
912
|
+
# table = CSV.parse(source, headers: true)
|
913
|
+
# table.by_row! # => #<CSV::Table mode:row row_count:4>
|
914
|
+
# table.each {|row| p row }
|
915
|
+
# Output:
|
916
|
+
# #<CSV::Row "Name":"foo" "Value":"0">
|
917
|
+
# #<CSV::Row "Name":"bar" "Value":"1">
|
918
|
+
# #<CSV::Row "Name":"baz" "Value":"2">
|
549
919
|
#
|
550
|
-
#
|
920
|
+
# When the access mode is <tt>:col</tt>,
|
921
|
+
# calls the block with each column as a 2-element array
|
922
|
+
# containing the header and an \Array of column fields:
|
923
|
+
# table.by_col! # => #<CSV::Table mode:col row_count:4>
|
924
|
+
# table.each {|column_data| p column_data }
|
925
|
+
# Output:
|
926
|
+
# ["Name", ["foo", "bar", "baz"]]
|
927
|
+
# ["Value", ["0", "1", "2"]]
|
551
928
|
#
|
929
|
+
# Returns a new \Enumerator if no block is given:
|
930
|
+
# table.each # => #<Enumerator: #<CSV::Table mode:col row_count:4>:each>
|
552
931
|
def each(&block)
|
553
932
|
return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
|
554
933
|
|
@@ -561,16 +940,40 @@ class CSV
|
|
561
940
|
self # for chaining
|
562
941
|
end
|
563
942
|
|
564
|
-
#
|
943
|
+
# :call-seq:
|
944
|
+
# table == other_table -> true or false
|
945
|
+
#
|
946
|
+
# Returns +true+ if all each row of +self+ <tt>==</tt>
|
947
|
+
# the corresponding row of +other_table+, otherwise, +false+.
|
948
|
+
#
|
949
|
+
# The access mode does no affect the result.
|
950
|
+
#
|
951
|
+
# Equal tables:
|
952
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
953
|
+
# table = CSV.parse(source, headers: true)
|
954
|
+
# other_table = CSV.parse(source, headers: true)
|
955
|
+
# table == other_table # => true
|
956
|
+
#
|
957
|
+
# Different row count:
|
958
|
+
# other_table.delete(2)
|
959
|
+
# table == other_table # => false
|
960
|
+
#
|
961
|
+
# Different last row:
|
962
|
+
# other_table << ['bat', 3]
|
963
|
+
# table == other_table # => false
|
565
964
|
def ==(other)
|
566
965
|
return @table == other.table if other.is_a? CSV::Table
|
567
966
|
@table == other
|
568
967
|
end
|
569
968
|
|
969
|
+
# :call-seq:
|
970
|
+
# table.to_a -> array_of_arrays
|
570
971
|
#
|
571
|
-
# Returns the table as an Array of Arrays
|
572
|
-
#
|
573
|
-
#
|
972
|
+
# Returns the table as an \Array of \Arrays;
|
973
|
+
# the headers are in the first row:
|
974
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
975
|
+
# table = CSV.parse(source, headers: true)
|
976
|
+
# table.to_a # => [["Name", "Value"], ["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
574
977
|
def to_a
|
575
978
|
array = [headers]
|
576
979
|
@table.each do |row|
|
@@ -580,13 +983,20 @@ class CSV
|
|
580
983
|
array
|
581
984
|
end
|
582
985
|
|
986
|
+
# :call-seq:
|
987
|
+
# table.to_csv(**options) -> csv_string
|
583
988
|
#
|
584
|
-
# Returns the table as
|
585
|
-
#
|
989
|
+
# Returns the table as \CSV string.
|
990
|
+
# See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
|
586
991
|
#
|
587
|
-
#
|
588
|
-
#
|
992
|
+
# Defaults option +write_headers+ to +true+:
|
993
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
994
|
+
# table = CSV.parse(source, headers: true)
|
995
|
+
# table.to_csv # => "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
589
996
|
#
|
997
|
+
# Omits the headers if option +write_headers+ is given as +false+
|
998
|
+
# (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
|
999
|
+
# table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
|
590
1000
|
def to_csv(write_headers: true, **options)
|
591
1001
|
array = write_headers ? [headers.to_csv(**options)] : []
|
592
1002
|
@table.each do |row|
|
@@ -615,7 +1025,18 @@ class CSV
|
|
615
1025
|
end
|
616
1026
|
end
|
617
1027
|
|
618
|
-
#
|
1028
|
+
# :call-seq:
|
1029
|
+
# table.inspect => string
|
1030
|
+
#
|
1031
|
+
# Returns a <tt>US-ASCII</tt>-encoded \String showing table:
|
1032
|
+
# - Class: <tt>CSV::Table</tt>.
|
1033
|
+
# - Access mode: <tt>:row</tt>, <tt>:col</tt>, or <tt>:col_or_row</tt>.
|
1034
|
+
# - Size: Row count, including the header row.
|
1035
|
+
#
|
1036
|
+
# Example:
|
1037
|
+
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
1038
|
+
# table = CSV.parse(source, headers: true)
|
1039
|
+
# table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>"
|
619
1040
|
def inspect
|
620
1041
|
"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
|
621
1042
|
end
|