Repository: mholt/PapaParse
Pull request #703 (2019-08-26 15:37:44)

Implemented quotes config optionally as function
Fix for issue #532. In the thread for #534, it was suggested that the `quotes` parameter be implemented as a function.


Patch:
diff --git a/docs/docs.html b/docs/docs.html
index 271a62a..647164e 100644
--- a/docs/docs.html
+++ b/docs/docs.html
@@ -282,7 +282,7 @@
 									<code>quotes</code>
 								</td>
 								<td>
-									If <code>true</code>, forces all fields to be enclosed in quotes. If an array of <code>true/false</code> values, specifies which fields should be force-quoted (first boolean is for the first column, second boolean for the second column, ...).
+									If <code>true</code>, forces all fields to be enclosed in quotes. If an array of <code>true/false</code> values, specifies which fields should be force-quoted (first boolean is for the first column, second boolean for the second column, ...). A function that returns a boolean values can be used to determine the quotes value of a cell. This function accepts the cell value and column index as parameters.
 								</td>
 							</tr>
 							<tr>
diff --git a/papaparse.js b/papaparse.js
index b1cb416..9f8a560 100755
--- a/papaparse.js
+++ b/papaparse.js
@@ -334,6 +334,7 @@ License: MIT
 			}
 
 			if (typeof _config.quotes === 'boolean'
+				|| typeof _config.quotes === 'function'
 				|| Array.isArray(_config.quotes))
 				_quotes = _config.quotes;
 
@@ -446,16 +447,17 @@ License: MIT
 			if (str.constructor === Date)
 				return JSON.stringify(str).slice(1, 25);
 
-			str = str.toString().replace(quoteCharRegex, _escapedQuote);
+			var escapedQuoteStr = str.toString().replace(quoteCharRegex, _escapedQuote);
 
 			var needsQuotes = (typeof _quotes === 'boolean' && _quotes)
+							|| (typeof _quotes === 'function' && _quotes(str, col))
 							|| (Array.isArray(_quotes) && _quotes[col])
-							|| hasAny(str, Papa.BAD_DELIMITERS)
-							|| str.indexOf(_delimiter) > -1
-							|| str.charAt(0) === ' '
-							|| str.charAt(str.length - 1) === ' ';
+							|| hasAny(escapedQuoteStr, Papa.BAD_DELIMITERS)
+							|| escapedQuoteStr.indexOf(_delimiter) > -1
+							|| escapedQuoteStr.charAt(0) === ' '
+							|| escapedQuoteStr.charAt(escapedQuoteStr.length - 1) === ' ';
 
-			return needsQuotes ? _quoteChar + str + _quoteChar : str;
+			return needsQuotes ? _quoteChar + escapedQuoteStr + _quoteChar : escapedQuoteStr;
 		}
 
 		function hasAny(str, substrings)


Test patch:
diff --git a/tests/test-cases.js b/tests/test-cases.js
index dadb724..0c4ab04 100644
--- a/tests/test-cases.js
+++ b/tests/test-cases.js
@@ -1711,6 +1711,18 @@ var UNPARSE_TESTS = [
 		config: { quotes: [true, false, true] },
 		expected: '"Col1",Col2,"Col3"\r\n"a",b,"c"\r\n"d",e,"f"'
 	},
+	{
+		description: "Force quotes around string fields only",
+		input: [['a', 'b', 'c'], ['d', 10, true]],
+		config: { quotes: function(value) { return typeof value === 'string'; } },
+		expected: '"a","b","c"\r\n"d",10,true'
+	},
+	{
+		description: "Force quotes around string fields only (with header row)",
+		input: [{ "Col1": "a", "Col2": "b", "Col3": "c" }, { "Col1": "d", "Col2": 10, "Col3": true }],
+		config: { quotes: function(value) { return typeof value === 'string'; } },
+		expected: '"Col1","Col2","Col3"\r\n"a","b","c"\r\n"d",10,true'
+	},
 	{
 		description: "Empty input",
 		input: [],