72

I've followed this post 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

asked Sep 17, 2013 at 11:37
0

11 Answers 11

99

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.

answered Sep 17, 2013 at 11:55
Sign up to request clarification or add additional context in comments.

13 Comments

Are you certain that string += is suboptimal? This article seems to say otherwise.
What about the fname? the files is just named Download, and its not a .csv file
If there is a '\n' or a ',' in one of the value, it will break.
@WalterTross Perfect! Thank you! That worked flawlessly! In addition, I went with link[1] to download the content. Basically using Blob and URL. [1] stackoverflow.com/a/24922761/334872
And, using this solution, I removed the first entry of csvContentArray ("assigning data:text......."), as it's not necessary anymore.
|
42

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

answered Feb 26, 2014 at 3:40

1 Comment

This answer assumes that there are no "special" characters in the array to be exported (no comma, no quotation mark). If there are, this answer won't work.
34

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, '');
answered Apr 30, 2015 at 19:51

3 Comments

Doesn't work.. Tested on data of second format in example.
is "values" defined somewhere?
@OGSean 'values' is 'Object.values'. See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. I updated code examples for clarity. Thanks
16

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.

marc_s
760k186 gold badges1.4k silver badges1.5k bronze badges
answered Jun 24, 2016 at 19:59

Comments

10

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))
answered Nov 17, 2016 at 14:41

4 Comments

The escape function is probably not 100% correct, but it should point you in the right direction.
To include a new line (\n) in CSV, you just need to enclose the string, containing new lines, in quotes. To enclose a string in quotes you need to escape the quotes in this string)
Error: fields.map is not a function
@étale-cohomology: It seems you are using a 1-dimensional array; instead you need an array of arrays.
8

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);

answered Apr 24, 2019 at 13:01

1 Comment

If data has # character it fails. Instead of encodeUri, encodeUriComponent solves the problem with # character. ` encodeURIComponent('a#b') "a%23b" encodeURI('a#b') "a#b" `
1

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));
});
answered Dec 7, 2016 at 21:16

Comments

0

ES6:

let csv = test_array.map(row=>row.join(',')).join('\n') 
//test_array being your 2D array
answered Oct 31, 2020 at 5:40

Comments

0

Try this:

const arrToCsv = arr => arr.map(row => row.map(s => (
 String(s).match(/,|"/) ? `"${s.replace(/"/g,'""')}"` : s
)).join`,`).join`\n`;
const arrObjToCsv = objArr => {
 if (!Array.isArray(objArr) || objArr.length === 0) return "";
 const headers = Object.keys(objArr[0]);
 return arrToCsv([
 headers,
 ...objArr.map(obj => headers.map(key => obj[key]))
 ]);
};
console.log(arrToCsv([
 ["name1", 2, 3],
 ["name2", 4, 5],
 ["name3", 6, 7],
 ["name4", 8, 9],
 ["name5", 10, 11]
]));
console.log(arrObjToCsv([
 { name: "name1", age: 22, count: 3 },
 { name: "name2", age: 23, count: 4 },
 { name: "name3", age: 24, count: 5 },
 { name: "name4", age: 25, count: 6 },
 { name: "name5", age: 26, count: 7 }
]));

This escapes double-quotes " and adds double-quotes if a comma or double-quote is found.

arrObjToCsv takes an array of objects with string or numeric values and uses its attributes as its headers.

answered May 2, 2024 at 0:03

Comments

-1
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
answered May 1, 2019 at 17:07

1 Comment

Doesn't work for me
-2

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

answered Jul 9, 2021 at 12:37

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.