Dart DocumentationCsvSheetCsvSheet

CsvSheet class

An indexable collection of CSV Values as a spreadsheet.

As spreadsheets (and unlike lists), a CsvSheet is indexed via column and then row, rather than row and column.

Columns can be referenced via their index number (1-based) or via their column header String.

CsvSheets can be visualized as such:

     1    2    3
  .----+----+----.
  |col1|col2|col3|
  +----+----+----+
1 |  1 |  2 |  3 |
  +----+----+----+
2 |  4 |  5 |  6 |
  +----+----+----+
3 |  7 |  8 |  9 |
  '----+----+----'

// access the value of cell 2,3 (in a spreadsheet as B3)
var value = csvSheet[2][3]; // 8

// If the cell has the header value of 'first name'
var value = csvSheet['col1'][2]; // 4
class CsvSheet {
 List _contents;
 HashMap _headers;
 _CsvColumn _fakeColumn;
 CsvRow _row;
 
 /// Return true if this sheet was created with a header row.
 bool hasHeaderRow;
 
 /**
  * Creates a CSV Sheet with the given contents. 
  * 
  * If headerRow is provided indicates that the first row of contents is a
  * header and not part of the data. Default is [false].
  * 
  * fieldSep is an optional field seperator if values are not comma separated.
  * 
  *     // tab separated sheet can be parsed with:
  *     var sheet = new CsvSheet(contents, fieldSep: '\t');
  * 
  * If you're using a file format which does not end lines with a simple '\n'
  * you can optionally specify what terminator rows are deliminated with.
  * 
  *     // Usually windows style should automatically be detected anyways.
  *     var sheet = new CsvSheet(contents, lineSep: '\r\n');
  */
 CsvSheet(String contents, 
     { bool headerRow: false,
     String fieldSep: ',',
     String lineSep: '\n' }) {
   _fakeColumn = new _CsvColumn(this);
   
   var allRows = contents.split(lineSep);
   hasHeaderRow = headerRow;
   
   if(hasHeaderRow) {
     // TODO: Should throw if a header name is repeated.
     var _rows = allRows[0].split(fieldSep);
     _headers = new HashMap();
     for(var i = 0; i < _rows.length; i ++) {
       var val = _rows[i];
       if(_rows.indexOf(val, i+1) != -1) {
         throw 
           new FormatException('The header column $val appears more than once');
       }
       _headers[_rows[i]] = i;
     }
     _row = new CsvRow(_headers);
     _contents = new List.generate(allRows.length - 1, (index) =>
         allRows[index+1].split(fieldSep).map((cell) => cell.trim()).toList());
   } else {
     _row = new CsvRow();
     _contents = new List.generate(allRows.length, (index) => 
       allRows[index].split(fieldSep).map((cell) => cell.trim()).toList());
   }
   while(_contents.last.length != _contents.first.length) {
     _contents.removeLast();
   }
 }
 
 /**
  * Access the column specified by [index]. Index may be a 1-based integer
  * value or a string matching one of the header rows.
  * 
  * Throws a [RangeError] if invalid index or if a String index is used but
  * [hasHeaderRow] is false.
  */
 operator [](index) {
   if(index is String) {
     var tmp = index;
     if(!hasHeaderRow) {
       throw new RangeError('$tmp is not a valid column header');
     }
     
     if(!_headers.containsKey(index)) throw new RangeError('$tmp is not a valid column header');
     index = _headers[index];
     _fakeColumn.row = index;
   } else {
     _fakeColumn.row = index-1;
   }
   
   return _fakeColumn;
 }
 
 /**
  * Iterate through each row in the spreadsheet as a CsvRow, calling 'action' 
  * for each row. It is an error if action tries to modify the list.
  * 
  * The CsvRow is indexable via the headers if applicable. 
  */
 void forEachRow(void action(CsvRow cells)) {
   _contents.forEach((row) {
     _row.row = row;
     action(_row);
   });
 }
 
 /**
  * Return the number of rows contained in this sheet. Does not included
  * the header row, if provided.
  */
 int get numRows => _contents.length;
 
 /**
  * Return the number of columns contained in this sheet.
  */
 int get numCols => _contents[0].length;
 
 // Used by _CsvColumn to access rows spreadsheet style instead of list style.
 _getValue(column, row) => _contents[row][column];
 
}

Constructors

new CsvSheet(String contents, {bool headerRow: false, String fieldSep: ',', String lineSep: '\n'}) #

Creates a CSV Sheet with the given contents.

If headerRow is provided indicates that the first row of contents is a header and not part of the data. Default is false.

fieldSep is an optional field seperator if values are not comma separated.

// tab separated sheet can be parsed with:
var sheet = new CsvSheet(contents, fieldSep: '\t');

If you're using a file format which does not end lines with a simple '\n' you can optionally specify what terminator rows are deliminated with.

// Usually windows style should automatically be detected anyways.
var sheet = new CsvSheet(contents, lineSep: '\r\n');
CsvSheet(String contents, 
   { bool headerRow: false,
   String fieldSep: ',',
   String lineSep: '\n' }) {
 _fakeColumn = new _CsvColumn(this);
 
 var allRows = contents.split(lineSep);
 hasHeaderRow = headerRow;
 
 if(hasHeaderRow) {
   // TODO: Should throw if a header name is repeated.
   var _rows = allRows[0].split(fieldSep);
   _headers = new HashMap();
   for(var i = 0; i < _rows.length; i ++) {
     var val = _rows[i];
     if(_rows.indexOf(val, i+1) != -1) {
       throw 
         new FormatException('The header column $val appears more than once');
     }
     _headers[_rows[i]] = i;
   }
   _row = new CsvRow(_headers);
   _contents = new List.generate(allRows.length - 1, (index) =>
       allRows[index+1].split(fieldSep).map((cell) => cell.trim()).toList());
 } else {
   _row = new CsvRow();
   _contents = new List.generate(allRows.length, (index) => 
     allRows[index].split(fieldSep).map((cell) => cell.trim()).toList());
 }
 while(_contents.last.length != _contents.first.length) {
   _contents.removeLast();
 }
}

Properties

bool hasHeaderRow #

Return true if this sheet was created with a header row.

bool hasHeaderRow

final int numCols #

Return the number of columns contained in this sheet.

int get numCols => _contents[0].length;

final int numRows #

Return the number of rows contained in this sheet. Does not included the header row, if provided.

int get numRows => _contents.length;

Operators

dynamic operator [](index) #

Access the column specified by index. Index may be a 1-based integer value or a string matching one of the header rows.

Throws a RangeError if invalid index or if a String index is used but hasHeaderRow is false.

operator [](index) {
 if(index is String) {
   var tmp = index;
   if(!hasHeaderRow) {
     throw new RangeError('$tmp is not a valid column header');
   }
   
   if(!_headers.containsKey(index)) throw new RangeError('$tmp is not a valid column header');
   index = _headers[index];
   _fakeColumn.row = index;
 } else {
   _fakeColumn.row = index-1;
 }
 
 return _fakeColumn;
}

Methods

void forEachRow(void action(CsvRow cells)) #

Iterate through each row in the spreadsheet as a CsvRow, calling 'action' for each row. It is an error if action tries to modify the list.

The CsvRow is indexable via the headers if applicable.

void forEachRow(void action(CsvRow cells)) {
 _contents.forEach((row) {
   _row.row = row;
   action(_row);
 });
}