Skip to the code
cf.

Parse a CSV line

Write parseCsvLine(line). It splits one line of CSV (comma-separated values) into an array of field strings.

Fields are separated by commas. Every comma outside quotes starts a new field, so a line with n such commas has n + 1 fields, and empty fields are kept: a,,b, gives ["a", "", "b", ""], and an empty line gives [""].

A field is quoted when its first character is ". A quoted field ends at the next " that isn't doubled; inside it, commas are ordinary characters and "" stands for one ". The surrounding quotes aren't part of the value. After the closing quote, the next character must be a comma or the end of the line.

Any other field is unquoted and kept exactly as written, spaces and " characters included. Throw a SyntaxError if a quoted field never closes, or if anything other than a comma follows its closing quote.

parseCsvLine('Ada,"Lovelace, Ada",36');
// ["Ada", "Lovelace, Ada", "36"]
parseCsvLine('"She said ""hi""",,x');
// ['She said "hi"', "", "x"]
parseCsvLine(' 5" , b ');
// [' 5" ', " b "]
parseCsvLine('"never closed, oops'); // throws a SyntaxError

What the tests check

  1. splits fields at commas
    expect(parseCsvLine("a,b,c")).toEqual(["a", "b", "c"]);
    expect(parseCsvLine("one")).toEqual(["one"]);
    
  2. keeps empty fields, including at the ends
    expect(parseCsvLine("a,,b,")).toEqual(["a", "", "b", ""]);
    expect(parseCsvLine(",x")).toEqual(["", "x"]);
    expect(parseCsvLine(",")).toEqual(["", ""]);
    expect(parseCsvLine("")).toEqual([""]);
    
  3. keeps commas inside quoted fields
    expect(parseCsvLine('Ada,"Lovelace, Ada",36')).toEqual(["Ada", "Lovelace, Ada", "36"]);
    expect(parseCsvLine('",,",","')).toEqual([",,", ","]);
    
  4. turns doubled quotes into one quote
    expect(parseCsvLine('"She said ""hi""",x')).toEqual(['She said "hi"', "x"]);
    expect(parseCsvLine('""""')).toEqual(['"']);
    expect(parseCsvLine('"a""b"')).toEqual(['a"b']);
    
  5. reads empty quoted fields
    expect(parseCsvLine('""')).toEqual([""]);
    expect(parseCsvLine('a,"",b')).toEqual(["a", "", "b"]);
    expect(parseCsvLine('"",""')).toEqual(["", ""]);
    
  6. keeps unquoted fields exactly as written
    expect(parseCsvLine(' a , b"c ,5"')).toEqual([" a ", ' b"c ', '5"']);
    expect(parseCsvLine(' "x",y')).toEqual([' "x"', "y"]);
    
  7. throws a SyntaxError when a quoted field never closes
    expect(() => parseCsvLine('"abc,def')).toThrow(SyntaxError);
    expect(() => parseCsvLine('a,"b""')).toThrow(SyntaxError);
    expect(() => parseCsvLine('"')).toThrow(SyntaxError);
    
  8. throws a SyntaxError when text follows a closing quote
    expect(() => parseCsvLine('"a"b,c')).toThrow(SyntaxError);
    expect(() => parseCsvLine('x,"a" ,c')).toThrow(SyntaxError);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).