JavaScript array to CSV

JavascriptArraysCsv

Javascript Problem Overview


I've followed this post https://stackoverflow.com/questions/14964035/how-to-export-javascript-array-info-to-csv-on-client-side to get a nested js array written as a csv file.

The array looks like:

var test_array = [["name1", 2, 3], ["name2", 4, 5], ["name3", 6, 7], ["name4", 8, 9], ["name5", 10, 11]];

The code given in the link works nicely except that after the third line of the csv file all the rest of the values are on the same line e.g.

name1,2,3
name2,4,5
name3,6,7
name4,8,9name5,10,11 etc etc

Can anyone shed any light on why this is? Same using Chrome or FF.

Thanks

EDIT

jsfiddle http://jsfiddle.net/iaingallagher/dJKz6/

Iain

Javascript Solutions


Solution 1 - Javascript

The cited answer was wrong. You had to change

csvContent += index < infoArray.length ? dataString+ "\n" : dataString;

to

csvContent += dataString + "\n";

As to why the cited answer was wrong (funny it has been accepted!): index, the second parameter of the forEach callback function, is the index in the looped-upon array, and it makes no sense to compare this to the size of infoArray, which is an item of said array (which happens to be an array too).

EDIT

Six years have passed now since I wrote this answer. Many things have changed, including browsers. The following was part of the answer:

START of aged part

BTW, the cited code is suboptimal. You should avoid to repeatedly append to a string. You should append to an array instead, and do an array.join("\n") at the end. Like this:

var lineArray = [];
data.forEach(function (infoArray, index) {
    var line = infoArray.join(",");
    lineArray.push(index == 0 ? "data:text/csv;charset=utf-8," + line : line);
});
var csvContent = lineArray.join("\n");

END of aged part

(Keep in mind that the CSV case is a bit different from generic string concatenation, since for every string you also have to add the separator.)

Anyway, the above seems not to be true anymore, at least not for Chrome and Firefox (it seems to still be true for Safari, though).

To put an end to uncertainty, I wrote a jsPerf test that tests whether, in order to concatenate strings in a comma-separated way, it's faster to push them onto an array and join the array, or to concatenate them first with the comma, and then directly with the result string using the += operator.

Please follow the link and run the test, so that we have enough data to be able to talk about facts instead of opinions.

Solution 2 - Javascript

General form is:

var ids = []; <= this is your array/collection
var csv = ids.join(",");

For your case you will have to adapt a little bit

Solution 3 - Javascript

for a simple csv one map() and a join() are enough:

var csv = test_array.map(function(d){
    return d.join();
}).join('\n');

/* Results in 
name1,2,3
name2,4,5
name3,6,7
name4,8,9
name5,10,11

This method also allows you to specify column separator other than a comma in the inner join. for example a tab: d.join('\t')

On the other hand if you want to do it properly and enclose strings in quotes "", then you can use some JSON magic:

var csv = test_array.map(function(d){
   return JSON.stringify(d);
})
.join('\n') 
.replace(/(^\[)|(\]$)/mg, ''); // remove opening [ and closing ] brackets from each line 

/* would produce
"name1",2,3
"name2",4,5
"name3",6,7
"name4",8,9
"name5",10,11

if you have array of objects like :

var data = [
  {"title": "Book title 1", "author": "Name1 Surname1"},
  {"title": "Book title 2", "author": "Name2 Surname2"},
  {"title": "Book title 3", "author": "Name3 Surname3"},
  {"title": "Book title 4", "author": "Name4 Surname4"}
];

// use
var csv = data.map(function(d){
    return JSON.stringify(Object.values(d));
})
.join('\n') 
.replace(/(^\[)|(\]$)/mg, '');

Solution 4 - Javascript

I created this code for creating a nice, readable csv files:

var objectToCSVRow = function(dataObject) {
	var dataArray = new Array;
	for (var o in dataObject) {
		var innerValue = dataObject[o]===null?'':dataObject[o].toString();
    	var result = innerValue.replace(/"/g, '""');
        result = '"' + result + '"';
		dataArray.push(result);
	}
	return dataArray.join(' ') + '\r\n';
}

var exportToCSV = function(arrayOfObjects) {

	if (!arrayOfObjects.length) {
		return;
	}

	var csvContent = "data:text/csv;charset=utf-8,";

	// headers
	csvContent += objectToCSVRow(Object.keys(arrayOfObjects[0]));

	arrayOfObjects.forEach(function(item){
		csvContent += objectToCSVRow(item);
	}); 

	var encodedUri = encodeURI(csvContent);
	var link = document.createElement("a");
	link.setAttribute("href", encodedUri);
	link.setAttribute("download", "customers.csv");
	document.body.appendChild(link); // Required for FF
	link.click();
	document.body.removeChild(link); 
}

In your case, since you use arrays in array instead of objects in array, You will skip the header part, but you could add the column names yourself by putting this instead of that part:

// headers
csvContent += '"Column name 1" "Column name 2" "Column name 3"\n';

The secret is that a space separates the columns in the csv file, and we put the column values in the double quotes to allow spaces, and escape any double quotes in the values themselves.

Also note that I replace null values with empty string, because that suited my needs, but you can change that and replace it with anything you like.

Solution 5 - Javascript

If your data contains any newlines or commas, you will need to escape those first:

const escape = text =>
    text.replace(/\\/g, "\\\\")
        .replace(/\n/g, "\\n")
        .replace(/,/g, "\\,")

escaped_array = test_array.map(fields => fields.map(escape))

Then simply do:

csv = escaped_array.map(fields => fields.join(","))
                .join("\n")

If you want to make it downloadable in-browser:

dl = "data:text/csv;charset=utf-8," + csv
window.open(encodeURI(dl))

Solution 6 - Javascript

The following code were written in ES6 and it will work in most of the browsers without an issue.

var test_array = [["name1", 2, 3], ["name2", 4, 5], ["name3", 6, 7], ["name4", 8, 9], ["name5", 10, 11]];

// Construct the comma seperated string
// If a column values contains a comma then surround the column value by double quotes
const csv = test_array.map(row => row.map(item => (typeof item === 'string' && item.indexOf(',') >= 0) ? `"${item}"`: String(item)).join(',')).join('\n');

// Format the CSV string
const data = encodeURI('data:text/csv;charset=utf-8,' + csv);

// Create a virtual Anchor tag
const link = document.createElement('a');
link.setAttribute('href', data);
link.setAttribute('download', 'export.csv');

// Append the Anchor tag in the actual web page or application
document.body.appendChild(link);

// Trigger the click event of the Anchor link
link.click();

// Remove the Anchor link form the web page or application
document.body.removeChild(link);

Solution 7 - Javascript

The selected answer is probably correct but it seems needlessly unclear.

I found Shomz's Fiddle to be very helpful, but again, needlessly unclear. (Edit: I now see that that Fiddle is based on the OP's Fiddle.)

Here's my version (which I've created a Fiddle for) which I think is more clear:

function downloadableCSV(rows) {
  var content = "data:text/csv;charset=utf-8,";

  rows.forEach(function(row, index) {
    content += row.join(",") + "\n";
  });

  return encodeURI(content);
}

var rows = [  ["name1", 2, 3],
  ["name2", 4, 5],
  ["name3", 6, 7],
  ["name4", 8, 9],
  ["name5", 10, 11]
];

$("#download").click(function() {
  window.open(downloadableCSV(rows));
});

Solution 8 - Javascript

ES6:

let csv = test_array.map(row=>row.join(',')).join('\n') 
//test_array being your 2D array

Solution 9 - Javascript

const escapeString = item => (typeof item === 'string') ? `"${item}"` : String(item)

const arrayToCsv = (arr, seperator = ';') => arr.map(escapeString).join(seperator)

const rowKeysToCsv = (row, seperator = ';') => arrayToCsv(Object.keys(row))

const rowToCsv = (row, seperator = ';') => arrayToCsv(Object.values(row))

const rowsToCsv = (arr, seperator = ';') => arr.map(row => rowToCsv(row, seperator)).join('\n')

const collectionToCsvWithHeading = (arr, seperator = ';') => `${rowKeysToCsv(arr[0], seperator)}\n${rowsToCsv(arr, seperator)}`


// Usage: 

collectionToCsvWithHeading([
  { title: 't', number: 2 },
  { title: 't', number: 1 }
])

// Outputs: 
"title";"number"
"t";2
"t";1

Solution 10 - Javascript

If you are using React and want to generate a CSV file on the client side, you can just do:

import CsvGenerator from '@exlabs/react-csv-generator';

const data = [{ id: 1, name: 'first' }, { id: 2, name: 'second' }];

const MyComponent = () => {
  return (
    <CsvGenerator fileName="my-name" items={data}>
      Download!
    </CsvGenerator>
  );
};

What is important, the package it's light and supports Excel and Numbers.

Link to the npm package

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionduffView Question on Stackoverflow
Solution 1 - JavascriptWalter TrossView Answer on Stackoverflow
Solution 2 - JavascriptMircea StanciuView Answer on Stackoverflow
Solution 3 - JavascriptKonstantinView Answer on Stackoverflow
Solution 4 - JavascriptFrane PoljakView Answer on Stackoverflow
Solution 5 - JavascriptZazView Answer on Stackoverflow
Solution 6 - JavascriptB.BalamanigandanView Answer on Stackoverflow
Solution 7 - JavascriptJason SwettView Answer on Stackoverflow
Solution 8 - JavascriptMichael SeltenreichView Answer on Stackoverflow
Solution 9 - JavascriptJelle HakView Answer on Stackoverflow
Solution 10 - JavascriptRiddView Answer on Stackoverflow