Created
May 4, 2025 08:57
-
-
Save yetti/d047c6e265863fabda551cffd83aa573 to your computer and use it in GitHub Desktop.
Object -> CSV or CSV -> Object
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Hash to CSV Converter</title> | |
<!-- Add PapaParse CDN --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js"></script> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
textarea { | |
width: 100%; | |
height: 200px; | |
margin-bottom: 15px; | |
font-family: monospace; | |
} | |
button { | |
padding: 8px 16px; | |
background-color: #4caf50; | |
color: white; | |
border: none; | |
border-radius: 4px; | |
cursor: pointer; | |
margin-right: 10px; | |
} | |
button:hover { | |
background-color: #45a049; | |
} | |
pre { | |
background-color: #f5f5f5; | |
padding: 10px; | |
overflow: auto; | |
border: 1px solid #ddd; | |
} | |
.file-input-container { | |
margin-top: 20px; | |
margin-bottom: 20px; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Hash to CSV Converter</h1> | |
<h2>Input JavaScript Object</h2> | |
<textarea id="hashInput"> | |
{ | |
"person1": { "name": "John", "age": 30, "city": "New York" }, | |
"person2": { "name": "Jane", "age": 25, "city": "Boston" }, | |
"person3": { "name": "Bob", "age": 35, "city": "Chicago" } | |
}</textarea | |
> | |
<div> | |
<button onclick="convertAndDownload()">Convert to CSV & Download</button> | |
</div> | |
<h2>Preview</h2> | |
<pre id="preview"></pre> | |
<h2>Load CSV Back to Object</h2> | |
<div class="file-input-container"> | |
<input type="file" id="csvFileInput" accept=".csv" /> | |
<button onclick="loadCSVFile()">Load CSV to Object</button> | |
</div> | |
<h3>Converted Object</h3> | |
<pre id="objectPreview"></pre> | |
<script> | |
// Convert JavaScript object to CSV string using PapaParse | |
function objectToCSV(obj) { | |
// Check if input is empty or invalid | |
if (!obj || Object.keys(obj).length === 0) { | |
return ""; | |
} | |
// Get all possible keys from all nested objects | |
const allKeys = new Set(); | |
Object.values(obj).forEach((item) => { | |
if (typeof item === "object" && item !== null) { | |
Object.keys(item).forEach((key) => allKeys.add(key)); | |
} | |
}); | |
const headers = ["id", ...Array.from(allKeys)]; | |
// Create array of row data for PapaParse | |
const rows = []; | |
// Add header row | |
rows.push(headers); | |
// Add data rows | |
Object.entries(obj).forEach(([key, value]) => { | |
const row = new Array(headers.length).fill(""); | |
row[0] = key; // Set ID | |
if (value && typeof value === "object") { | |
Object.entries(value).forEach(([propKey, propValue]) => { | |
const headerIndex = headers.indexOf(propKey); | |
if (headerIndex > 0) { | |
row[headerIndex] = propValue; | |
} | |
}); | |
} | |
rows.push(row); | |
}); | |
// Use PapaParse to generate CSV | |
const csvContent = Papa.unparse(rows, { | |
quotes: true, // Use quotes around fields that need them | |
quoteChar: '"', | |
delimiter: ",", | |
header: false, // Already included headers in our data | |
newline: "\n", | |
}); | |
return csvContent; | |
} | |
function convertAndDownload() { | |
try { | |
// Get the input | |
const input = document.getElementById("hashInput").value.trim(); | |
// Safely evaluate the JavaScript object | |
let data; | |
if (input.startsWith("{") && input.endsWith("}")) { | |
// Use a safer approach than Function constructor | |
try { | |
data = JSON.parse(input); | |
} catch (jsonError) { | |
// If JSON.parse fails, try evaluating as a JavaScript object | |
try { | |
// Use Function but with extra validation | |
if (!/[<>]|script|on\w+=|document\./.test(input)) { | |
data = Function("return " + input)(); | |
} else { | |
throw new Error("Input contains potentially unsafe code"); | |
} | |
} catch (evalError) { | |
throw new Error( | |
"Invalid JavaScript object: " + evalError.message | |
); | |
} | |
} | |
} else { | |
throw new Error("Input must be a valid JavaScript object"); | |
} | |
// Convert to CSV using PapaParse | |
const csvContent = objectToCSV(data); | |
// Show preview | |
document.getElementById("preview").textContent = csvContent; | |
// Create download link | |
const blob = new Blob([csvContent], { | |
type: "text/csv;charset=utf-8;", | |
}); | |
const url = URL.createObjectURL(blob); | |
const link = document.createElement("a"); | |
link.setAttribute("href", url); | |
link.setAttribute("download", "data.csv"); | |
link.style.display = "none"; | |
// Add to document, click and remove | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
} catch (error) { | |
alert("Error: " + error.message); | |
console.error(error); | |
} | |
} | |
// Convert CSV string back to JavaScript object using PapaParse | |
function loadCSVFile() { | |
const fileInput = document.getElementById("csvFileInput"); | |
const file = fileInput.files[0]; | |
if (!file) { | |
alert("Please select a CSV file first."); | |
return; | |
} | |
Papa.parse(file, { | |
complete: function (results) { | |
try { | |
if (results.errors.length > 0) { | |
console.warn("CSV parsing had errors:", results.errors); | |
} | |
const data = results.data; | |
// First row should be headers | |
if (data.length < 2) { | |
throw new Error( | |
"CSV file must have headers and at least one data row" | |
); | |
} | |
const headers = data[0]; | |
const idIndex = headers.findIndex( | |
(header) => header.toLowerCase() === "id" | |
); | |
if (idIndex === -1) { | |
throw new Error('CSV must contain "id" column'); | |
} | |
// Convert CSV data to object | |
const reconstructedObject = {}; | |
// Process each row (skip header row) | |
for (let i = 1; i < data.length; i++) { | |
const row = data[i]; | |
// Skip empty rows | |
if (row.length <= 1 && (row[0] === "" || row[0] === undefined)) | |
continue; | |
const id = row[idIndex]; | |
if (!id) continue; // Skip rows without ID | |
reconstructedObject[id] = {}; | |
// Add all properties from the row | |
for (let j = 0; j < headers.length; j++) { | |
if (j !== idIndex && headers[j] && row[j] !== "") { | |
let value = row[j]; | |
// Try to convert numeric strings to numbers | |
if (value && /^-?\d+(\.\d+)?$/.test(value)) { | |
value = parseFloat(value); | |
} | |
reconstructedObject[id][headers[j]] = value; | |
} | |
} | |
} | |
// Display the resulting object | |
document.getElementById("objectPreview").textContent = | |
JSON.stringify(reconstructedObject, null, 2); | |
// Optionally, populate the input textarea | |
document.getElementById("hashInput").value = JSON.stringify( | |
reconstructedObject, | |
null, | |
2 | |
); | |
} catch (error) { | |
alert("Error processing CSV: " + error.message); | |
console.error(error); | |
} | |
}, | |
error: function (error) { | |
alert("Error reading CSV file: " + error.message); | |
console.error(error); | |
}, | |
}); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment