JavaScript 数组到 CSV
Posted
技术标签:
【中文标题】JavaScript 数组到 CSV【英文标题】:JavaScript array to CSV 【发布时间】:2013-09-21 19:59:54 【问题描述】:我已经关注了这篇帖子How to export javascript array info to csv (on client side)? 来获取一个嵌套的 js 数组,该数组写为 csv 文件。
数组看起来像:
var test_array = [["name1", 2, 3], ["name2", 4, 5], ["name3", 6, 7], ["name4", 8, 9], ["name5", 10, 11]];
链接中给出的代码运行良好,只是在 csv 文件的第三行之后,所有其余值都在同一行 例如
名称1,2,3 名称2,4,5 名称3,6,7 name4,8,9name5,10,11 等
谁能解释这是为什么?同样使用 Chrome 或 FF。
谢谢
编辑
jsfiddle http://jsfiddle.net/iaingallagher/dJKz6/
伊恩
【问题讨论】:
【参考方案1】:如果你使用 React 并且想在客户端生成一个 CSV 文件,你可以这样做:
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>
);
;
重要的是,这个包很轻,支持 Excel 和 Numbers。
Link to the npm package
【讨论】:
【参考方案2】:ES6:
let csv = test_array.map(row=>row.join(',')).join('\n')
//test_array being your 2D array
【讨论】:
【参考方案3】:引用的答案是错误的。你必须改变
csvContent += index < infoArray.length ? dataString+ "\n" : dataString;
到
csvContent += dataString + "\n";
至于为什么引用的答案是错误的(有趣的是它被接受了!):index
,forEach
回调函数的第二个参数,是循环数组中的索引,没有意义将其与infoArray
的大小进行比较,infoArray
是所述数组的一项(恰好也是一个数组)。
编辑
自从我写下这个答案以来,已经过去了六年。许多事情都发生了变化,包括浏览器。以下是部分答案:
老化部分的开始
顺便说一句,引用的代码不是最佳的。您应该避免重复附加到字符串。您应该改为附加到数组,并在最后执行 array.join("\n") 。像这样:
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");
老化部分结束
(请记住,CSV 大小写与通用字符串连接有点不同,因为您还必须为每个字符串添加分隔符。)
不管怎样,上述情况似乎不不再正确,至少对于 Chrome 和 Firefox 而言并非如此(不过对于 Safari 来说似乎仍然如此)。
为了结束不确定性,我写了一个jsPerf test 来测试,为了以逗号分隔的方式连接字符串,将它们推入数组并加入数组,或者先连接它们是否更快使用逗号,然后使用 += 运算符直接使用结果字符串。
请点击链接并运行测试,以便我们有足够的数据来谈论事实而不是意见。
【讨论】:
您确定字符串 += 不是最优的吗? This article 似乎另有说法。 fname 怎么样?这些文件只是命名为下载,而不是 .csv 文件 如果其中一个值中有'\n'或',',它将中断。 @WalterTross 完美!谢谢!那完美无瑕!此外,我使用链接 [1] 下载内容。基本上使用Blob
和URL
。 [1]***.com/a/24922761/334872
并且,使用这个解决方案,我删除了 csvContentArray 的第一个条目(“assigning data:text.......”),因为它不再需要了。【参考方案4】:
对于一个简单的 csv,一个 map() 和一个 join() 就足够了:
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
此方法还允许您在内部join
中指定除逗号以外的列分隔符。例如标签:d.join('\t')
另一方面,如果您想正确执行此操作并将字符串括在引号 ""
中,那么您可以使用一些 JSON 魔术:
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
如果你有像这样的对象数组:
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, '');
【讨论】:
不起作用。在示例中对第二种格式的数据进行了测试。 是否在某处定义了“值”? @OGSean 'values' 是 'Object.values'。见developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。为了清楚起见,我更新了代码示例。谢谢【参考方案5】: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
【讨论】:
对我不起作用【参考方案6】:以下代码是用 ES6 编写的,它可以在大多数浏览器中正常运行。
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);
【讨论】:
如果数据有 # 字符失败。代替 encodeUri,encodeUriComponent 解决了 # 字符的问题。 `encodeURIComponent('a#b') "a%23b" encodeURI('a#b') "a#b" `【参考方案7】:我创建了这段代码来创建一个漂亮、可读的 csv 文件:
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);
在您的情况下,由于您在数组中使用数组而不是数组中的对象,因此您将跳过标题部分,但您可以自己添加列名,方法是放置这个而不是那个部分:
// headers
csvContent += '"Column name 1" "Column name 2" "Column name 3"\n';
秘诀是用空格分隔 csv 文件中的列,我们将列值放在双引号中以允许空格,并转义值本身中的任何双引号。
另外请注意,我用空字符串替换空值,因为这符合我的需要,但您可以更改它并用您喜欢的任何内容替换它。
【讨论】:
【参考方案8】:选择的答案可能是正确的,但似乎不必要地不清楚。
我发现Shomz's Fiddle 非常有帮助,但又一次不必要地不清楚。 (编辑:我现在看到 Fiddle 是基于 OP 的 Fiddle。)
这是我认为更清楚的版本(我已经为它创建了一个Fiddle):
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));
);
【讨论】:
【参考方案9】:如果您的数据包含任何换行符或逗号,您需要先将其转义:
const escape = text =>
text.replace(/\\/g, "\\\\")
.replace(/\n/g, "\\n")
.replace(/,/g, "\\,")
escaped_array = test_array.map(fields => fields.map(escape))
然后简单地做:
csv = escaped_array.map(fields => fields.join(","))
.join("\n")
如果你想让它在浏览器中可下载:
dl = "data:text/csv;charset=utf-8," + csv
window.open(encodeURI(dl))
【讨论】:
转义函数可能不是 100% 正确,但它应该为您指明正确的方向。 要在 CSV 中包含新行 (\n),您只需将包含新行的字符串用引号括起来。要将字符串括在引号中,您需要转义此字符串中的引号) 错误:fields.map
不是函数【参考方案10】:
一般形式是:
var ids = []; <= this is your array/collection
var csv = ids.join(",");
对于您的情况,您将不得不稍作调整
【讨论】:
此答案假定要导出的数组中没有“特殊”字符(没有逗号,没有引号)。如果有,此答案将不起作用。以上是关于JavaScript 数组到 CSV的主要内容,如果未能解决你的问题,请参考以下文章
在JavaScript中将一个数组附加到另一个数组[重复]