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.
@@ -4,12 +4,12 @@ require "forwardable"
4
4
 
5
5
  class CSV
6
6
  # = \CSV::Table
7
- # A \CSV::Table instance is an object representing \CSV data.
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: each is a Table::Row object.
12
- # - Headers: names for the columns.
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
- # In the default mixed mode, this method returns rows for index access and
370
- # columns for header access. You can force the index association by first
371
- # calling by_col!() or by_row!().
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
- # Columns are returned as an Array of values. Altering that Array has no
374
- # effect on the table.
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
- # In the default mixed mode, this method assigns rows for index access and
387
- # columns for header access. You can force the index association by first
388
- # calling by_col!() or by_row!().
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
- # Rows may be set to an Array of values (which will inherit the table's
391
- # headers()) or a CSV::Row.
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
- # Columns may be set to a single value, which is copied to each row of the
394
- # column, or an Array of values. Arrays of values are assigned to rows top
395
- # to bottom in row major order. Excess values are ignored and if the Array
396
- # does not have a value for each row the extra rows will receive a +nil+.
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
- # Assigning to an existing column or row clobbers the data. Assigning to
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
- # The mixed mode default is to treat a list of indices as row access,
436
- # returning the rows indicated. Anything else is considered columnar
437
- # access. For columnar access, the return set has an Array for each row
438
- # with the values indicated by the headers in each Array. You can force
439
- # column or row mode using by_col!() or by_row!().
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
- # You cannot mix column and row access.
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
- # Adds a new row to the bottom end of this table. You can provide an Array,
459
- # which will be converted to a CSV::Row (inheriting the table's headers()),
460
- # or a CSV::Row.
461
- #
462
- # This method returns the table for chaining.
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
- # A shortcut for appending multiple rows. Equivalent to:
476
- #
477
- # rows.each { |row| self << row }
773
+ # :call-seq:
774
+ # table.push(*rows_or_arrays) -> self
478
775
  #
479
- # This method returns the table for chaining.
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
- # Removes and returns the indicated columns or rows. In the default mixed
489
- # mode indices refer to rows and everything else is assumed to be a column
490
- # headers. Use by_col!() or by_row!() to force the lookup.
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 any column or row for which the block returns +true+. In the
518
- # default mixed mode or row mode, iteration is the standard row major
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
- # This method returns the table for chaining.
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
- # If no block is given, an Enumerator is returned.
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
- # In the default mixed mode or row mode, iteration is the standard row major
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
- # This method returns the table for chaining.
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
- # If no block is given, an Enumerator is returned.
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
- # Returns +true+ if all rows of this table ==() +other+'s rows.
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. Headers will be the first row,
572
- # then all of the field rows will follow.
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 a complete CSV String. Headers will be listed first,
585
- # then all of the field rows.
989
+ # Returns the table as \CSV string.
990
+ # See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
586
991
  #
587
- # This method assumes you want the Table.headers(), unless you explicitly
588
- # pass <tt>:write_headers => false</tt>.
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
- # Shows the mode and size of this table in a US-ASCII String.
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