JSON.stringify() is a JavaScript method used to convert a JavaScript object or value to a JSON string.
https://bigfrontend.dev/problem/implement-JSON-stringify
I believe you've used JSON.stringify()
before, do you know the details of how it handles arbitrary data?
Please have a guess on the details and then take a look at the explanation on MDN, it is actually pretty complex.
In this problem, you are asked to implement your own version of JSON.stringify()
.
In a real interview, you are not expected to cover all the cases, just decide the scope with interviewer. But for a goal of practicing, your code here will be tested against a lot of data types. Please try to cover as much as you can.
Attention to the circular reference.
note
JSON.stringify()
support two more parameters which is not required here.
/**
* @param {any} data
* @return {string}
*/
function stringify(data) {
const typeOfData = detectDataType(data);
if (typeOfData === 'array') {
return stringifyArr(data);
}
if (typeOfData === 'object' || typeOfData === 'map') {
return stringifyObj(data);
}
return _stringify(typeOfData, data);
}
function stringifyObj(data) {
let stringifiedData = [];
for (const key of Object.keys(data)) {
const val = data[key];
const typeOfVal = detectDataType(val);
if (
typeOfVal === 'symbol' ||
typeOfVal === 'function' ||
typeOfVal === 'undefined'
) {
continue;
}
let stringifiedKey = `\"${key}\":`;
switch (typeOfVal) {
case 'array':
stringifiedKey += stringifyArr(val);
break;
case 'object':
case 'map':
stringifiedKey += stringifyObj(val);
break;
default:
stringifiedKey += _stringify(typeOfVal, val);
}
stringifiedData.push(stringifiedKey);
}
return `{${stringifiedData.join(',')}}`;
}
function stringifyArr(data) {
let stringifiedData = [];
for (const [index, val] of data.entries()) {
if (isNaN(index)) {
continue;
}
const typeOfVal = detectDataType(val);
switch (typeOfVal) {
case 'array':
stringifiedData.push(stringifyArr(val));
break;
case 'object':
case 'map':
stringifiedData.push(stringifyObj(val));
break;
default:
stringifiedData.push(_stringify(typeOfVal, val));
}
}
return `[${stringifiedData.join(',')}]`;
}
function _stringify(typeOfData, data) {
switch (typeOfData) {
case 'string':
return `\"${data}\"`;
case 'number':
case 'boolean':
return String(data);
case 'function':
return undefined;
case 'date':
return `"${data.toISOString()}"`;
case 'set':
case 'map':
case 'weakSet':
case 'weakMap':
return '{}';
case 'bigint':
throw new Error("TypeError: BigInt value can't be serialized in JSON");
default:
return 'null';
}
}
const dataTypes = new Map([
[Number, 'number'],
[String, 'string'],
[Boolean, 'boolean'],
[Array, 'array'],
[ArrayBuffer, 'arraybuffer'],
[Date, 'date'],
[Set, 'set'],
[Map, 'map'],
[WeakSet, 'weakSet'],
[WeakMap, 'weakMap'],
]);
function detectDataType(data) {
if (typeof data === 'number' && isNaN(data)) {
return 'NaN';
}
if (data === Infinity) {
return 'infinity';
}
if (typeof data !== 'object') {
return typeof data;
}
if (data === null) {
return 'null';
}
for (const [type, name] of dataTypes.entries()) {
if (data instanceof type) {
return name;
}
}
return 'object';
}
console.log(stringify({ x: 5, y: 6 })); // {"x":5,"y":6}
console.log(stringify([1, 2, 3])); // [1,2,3]
console.log(stringify("test")); // "test"
console.log(stringify(5)); // 5
console.log(stringify(true)); // true
console.log(stringify(null)); // null
console.log(stringify(new Date())); // "2021-07-09T20:22:30.000Z"
console.log(stringify(new Set([1, 2, 3]))); // {}
console.log(stringify(new Map([["key", "value"]]))); // {}
console.log(stringify(new WeakSet())); // {}
console.log(stringify(new WeakMap())); // {}
console.log(stringify(new ArrayBuffer(10))); // {}
console.log(stringify(NaN)); // NaN
console.log(stringify(Infinity)); // infinity
console.log(stringify(BigInt(10))); // throws TypeError
console.log(stringify(Symbol("test"))); // {}
console.log(stringify(() => {})); // undefined
console.log(stringify(undefined)); // undefined
This code is a custom implementation of JSON.stringify() function in JavaScript. It converts JavaScript values into a JSON string. Here's a breakdown of the main functions:
-
stringify(data)
: This is the main function that takes a JavaScript value and returns a JSON string. It first detects the type of the data usingdetectDataType(data)
, and then calls the appropriate function to stringify the data. -
stringifyObj(data)
: This function stringifies a JavaScript object. It iterates over the keys of the object, detects the type of each value, and then stringifies the value. It ignores functions, symbols, and undefined values. The resulting key-value pairs are joined into a string with comma separators and enclosed in curly braces. -
stringifyArr(data)
: This function stringifies a JavaScript array. It iterates over the elements of the array, detects the type of each element, and then stringifies the element. The resulting elements are joined into a string with comma separators and enclosed in square brackets. -
_stringify(typeOfData, data)
: This function stringifies a JavaScript value based on its type. It handles string, number, boolean, date, set, map, weakSet, weakMap, and bigint types. It throws an error for bigint values because they can't be serialized in JSON. -
detectDataType(data)
: This function detects the type of a JavaScript value. It uses a map of constructors to type names for complex types, and thetypeof
operator for simple types. It also handles special cases like NaN and Infinity.
Here are some real-world examples of its use:
When working with web applications, you might want to store complex data structures in the browser's local storage. Since local storage can only store strings, you need to use JSON.stringify() to convert objects to strings before storing them.
Example:
const user = {
name: "Alice",
age: 25,
preferences: {
theme: "dark",
language: "en"
}
};
// Convert the user object to a JSON string
const userString = JSON.stringify(user);
// Store the JSON string in local storage
localStorage.setItem('user', userString);
// Later, you can retrieve it and parse it back to an object
const retrievedUserString = localStorage.getItem('user');
const retrievedUser = JSON.parse(retrievedUserString);
console.log(retrievedUser);
// Output: { name: "Alice", age: 25, preferences: { theme: "dark", language: "en" } }
When sending data to a server via an HTTP request, you often need to send JSON. Using JSON.stringify(), you can convert your data into a JSON string before sending it.
Example:
const product = {
id: 123,
name: "Laptop",
price: 999.99,
inStock: true
};
// Convert the product object to a JSON string
const productString = JSON.stringify(product);
// Send the JSON string to the server using fetch
fetch('https://api.example.com/products', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: productString
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
When debugging, you might want to log objects as strings for easier readability or to include them in error messages.
Example:
const errorDetails = {
message: "Something went wrong",
code: 500,
timestamp: new Date()
};
// Convert the error details object to a JSON string for logging
console.error(JSON.stringify(errorDetails));
// Output: {"message":"Something went wrong","code":500,"timestamp":"2023-04-26T08:34:00.000Z"}
While JSON.stringify() and JSON.parse() can be used for deep copying objects, it's important to note that this method has limitations, such as not supporting functions or special object types (e.g., Date, undefined).
Example:
const original = {
name: "Original",
details: {
age: 30,
active: true
}
};
// Create a deep copy using JSON.stringify() and JSON.parse()
const copy = JSON.parse(JSON.stringify(original));
// Modify the copy
copy.details.age = 40;
console.log(original.details.age); // Output: 30
console.log(copy.details.age); // Output: 40
You can use JSON.stringify() to format JSON strings with indentation for better readability, useful for generating human-readable logs or documents.
Example:
const data = {
id: 1,
name: "Test",
items: ["item1", "item2", "item3"]
};
// Convert the data object to a pretty-printed JSON string
const prettyDataString = JSON.stringify(data, null, 2);
console.log(prettyDataString);
// Output:
// {
// "id": 1,
// "name": "Test",
// "items": [
// "item1",
// "item2",
// "item3"
// ]
// }