Skip to content

Commit

Permalink
feat: implementation for JSON.stringify()
Browse files Browse the repository at this point in the history
  • Loading branch information
Shaban-Eissa committed Jun 1, 2024
1 parent b3a1086 commit fec1f2d
Showing 1 changed file with 338 additions and 0 deletions.
338 changes: 338 additions & 0 deletions 21.implement-JSON-stringify.md
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"
// ]
// }
```

0 comments on commit fec1f2d

Please sign in to comment.