csv 3.1.7 → 3.2.1

Sign up to get free protection for your applications and to get access to all the features.
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 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}
551
+ #
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}
557
+ #
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}
566
+ #
567
+ # Note that the +nil+ rows are actually +nil+, not a row of +nil+ fields.
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>
389
627
  #
390
- # Rows may be set to an Array of values (which will inherit the table's
391
- # headers()) or a CSV::Row.
628
+ # If there are too few values, fills with +nil+ values:
629
+ # table['Value'] = [0]
630
+ # table['Value'] # => [0, nil, nil]
392
631
  #
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+.
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]
397
635
  #
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.
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!
400
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
797
+ #
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">]
820
+ #
821
+ # ---
487
822
  #
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.
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"]
491
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,20 +901,40 @@ 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
 
555
934
  if @mode == :col
556
- headers.each { |header| yield([header, self[header]]) }
935
+ headers.each.with_index do |header, i|
936
+ yield([header, @table.map {|row| row[header, i]}])
937
+ end
557
938
  else
558
939
  @table.each(&block)
559
940
  end
@@ -561,16 +942,40 @@ class CSV
561
942
  self # for chaining
562
943
  end
563
944
 
564
- # Returns +true+ if all rows of this table ==() +other+'s rows.
945
+ # :call-seq:
946
+ # table == other_table -> true or false
947
+ #
948
+ # Returns +true+ if all each row of +self+ <tt>==</tt>
949
+ # the corresponding row of +other_table+, otherwise, +false+.
950
+ #
951
+ # The access mode does no affect the result.
952
+ #
953
+ # Equal tables:
954
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
955
+ # table = CSV.parse(source, headers: true)
956
+ # other_table = CSV.parse(source, headers: true)
957
+ # table == other_table # => true
958
+ #
959
+ # Different row count:
960
+ # other_table.delete(2)
961
+ # table == other_table # => false
962
+ #
963
+ # Different last row:
964
+ # other_table << ['bat', 3]
965
+ # table == other_table # => false
565
966
  def ==(other)
566
967
  return @table == other.table if other.is_a? CSV::Table
567
968
  @table == other
568
969
  end
569
970
 
971
+ # :call-seq:
972
+ # table.to_a -> array_of_arrays
570
973
  #
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
- #
974
+ # Returns the table as an \Array of \Arrays;
975
+ # the headers are in the first row:
976
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
977
+ # table = CSV.parse(source, headers: true)
978
+ # table.to_a # => [["Name", "Value"], ["foo", "0"], ["bar", "1"], ["baz", "2"]]
574
979
  def to_a
575
980
  array = [headers]
576
981
  @table.each do |row|
@@ -580,13 +985,20 @@ class CSV
580
985
  array
581
986
  end
582
987
 
988
+ # :call-seq:
989
+ # table.to_csv(**options) -> csv_string
583
990
  #
584
- # Returns the table as a complete CSV String. Headers will be listed first,
585
- # then all of the field rows.
991
+ # Returns the table as \CSV string.
992
+ # See {Options for Generating}[../CSV.html#class-CSV-label-Options+for+Generating].
586
993
  #
587
- # This method assumes you want the Table.headers(), unless you explicitly
588
- # pass <tt>:write_headers => false</tt>.
994
+ # Defaults option +write_headers+ to +true+:
995
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
996
+ # table = CSV.parse(source, headers: true)
997
+ # table.to_csv # => "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
589
998
  #
999
+ # Omits the headers if option +write_headers+ is given as +false+
1000
+ # (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
1001
+ # table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
590
1002
  def to_csv(write_headers: true, **options)
591
1003
  array = write_headers ? [headers.to_csv(**options)] : []
592
1004
  @table.each do |row|
@@ -615,7 +1027,18 @@ class CSV
615
1027
  end
616
1028
  end
617
1029
 
618
- # Shows the mode and size of this table in a US-ASCII String.
1030
+ # :call-seq:
1031
+ # table.inspect => string
1032
+ #
1033
+ # Returns a <tt>US-ASCII</tt>-encoded \String showing table:
1034
+ # - Class: <tt>CSV::Table</tt>.
1035
+ # - Access mode: <tt>:row</tt>, <tt>:col</tt>, or <tt>:col_or_row</tt>.
1036
+ # - Size: Row count, including the header row.
1037
+ #
1038
+ # Example:
1039
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1040
+ # table = CSV.parse(source, headers: true)
1041
+ # table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>"
619
1042
  def inspect
620
1043
  "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
621
1044
  end