-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implementation for JSON.stringify()
- Loading branch information
1 parent
b3a1086
commit fec1f2d
Showing
1 changed file
with
338 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,338 @@ | ||
# 21. implement JSON.stringify() | ||
JSON.stringify() is a JavaScript method used to convert a JavaScript object or value to a JSON string. | ||
|
||
### Problem | ||
|
||
https://bigfrontend.dev/problem/implement-JSON-stringify | ||
|
||
# | ||
|
||
### Problem Description | ||
|
||
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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify), 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. | ||
|
||
# | ||
|
||
### Solution | ||
|
||
```js | ||
/** | ||
* @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'; | ||
} | ||
``` | ||
|
||
# | ||
|
||
### Usage | ||
|
||
```js | ||
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 | ||
``` | ||
|
||
|
||
# | ||
|
||
### Real World Examples | ||
|
||
Here are some real-world examples of its use: | ||
|
||
### 1. Storing Data in Local Storage | ||
|
||
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:** | ||
|
||
```javascript | ||
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" } } | ||
``` | ||
|
||
### 2. Sending Data to a Server | ||
|
||
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:** | ||
|
||
```javascript | ||
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)); | ||
``` | ||
|
||
### 3. Logging Data for Debugging | ||
|
||
When debugging, you might want to log objects as strings for easier readability or to include them in error messages. | ||
|
||
**Example:** | ||
|
||
```javascript | ||
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"} | ||
``` | ||
|
||
### 4. Deep Copying Objects | ||
|
||
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:** | ||
|
||
```javascript | ||
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 | ||
``` | ||
|
||
### 5. Formatting Output with Indentation | ||
|
||
You can use JSON.stringify() to format JSON strings with indentation for better readability, useful for generating human-readable logs or documents. | ||
|
||
**Example:** | ||
|
||
```javascript | ||
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" | ||
// ] | ||
// } | ||
``` | ||
|