Recursive methods using Javascript

I am trying to replicate the json stringify method, but use recursion instead. I was able to go through many tests, but when it comes to nested arrays, I seem to be having problems. if there is an empty array in the array ('[]'), I get something like [, 7,9] instead of [[], 7,9]. Also, if I pass:

stringifyJSON([[["test","mike",4,["jake"]],3,4]]) "[[test,mike,4,jake,3,4]]" 

I thought I was close to getting this to work, but I might have to start all over again. Do you have ideas on what I can change to make this work for nested examples? Here is the code that I have now:

 var testarray = [9,[[],2,3]] //should return '[9,[[],2,3]]' var count = 0 var stringifyJSON = function(obj,stack) { var typecheck = typeof obj; var resarray = stack; if(resarray == null){ //does resarray exist? Is this the first time through? var resarray = []; } if(typeof obj === "string"){ //Is obj a string? return '"' + String(obj) + '"'; } if((Array.isArray(obj)) && (obj.length > 0)){ //If not a string, is it an object? for(var i = 0; i<obj.length;i++){ if(Array.isArray(obj[i])){ var arraytemp = [] stringifyJSON(arraytemp.push(obj[i]),resarray) // this is probably incorrect, this is how i handle a nested array situation } if(typeof obj[i] === 'number'){ //if the number is inside of the array, don't quote it resarray.push(obj[i]); } else if(typecheck === 'object' && Array.isArray(obj[0])){ resarray.push('[' + obj[i] + ']'); } else{ resarray.push('"' + obj[i] + '"'); } obj.shift() //delete the first object in the array and get ready to recurse to get to the second object. stringifyJSON(obj,resarray); //remember the new array when recursing by passing it into the next recursive instance } } if(obj !== null && typeof obj === 'object'){ //is obj an object? for(var key in obj){ stringifyJSON(resarray.push(key + '"' + ':' + obj[key]),resarray) } } if(typeof obj === "number" || obj == null || obj === true || obj === false){ //special cases and if it a number return '' + obj + '' } if(typecheck === 'object'){ //a special case where you have an empty array that needs to be quoted. return '['+resarray+']' } return '' + resarray.join('') + ''; }; //JSON values cannot be a function, a date, or undefined 
+1
json javascript arrays recursion
Mar 09 '17 at 5:58 on
source share
2 answers

Do you have ideas on what I can change to make this work for nested examples?

Of course, but you will have to abandon the entire function, so I hope you do not mind. I will give a list of letters, why this approach is necessary, and yours, in essence, is wrong from get-go :(




Corner cases

This function performs a simple case analysis for the non-null data constructor property and encodes accordingly. He manages to cover many angular cases that you are unlikely to consider, for example

  • JSON.stringify(undefined) returns undefined
  • JSON.stringify(null) returns 'null'
  • JSON.stringify(true) returns 'true'
  • JSON.stringify([1,2,undefined,4]) returns '[1,2,null,4]'
  • JSON.stringify({a: undefined, b: 2}) returns '{ "b": 2 }'
  • JSON.stringify({a: /foo/}) returns { "a": {} }

So, to make sure our stringifyJSON function works fine, I will not test its output directly. Instead, I will write a small test method that ensures that the JSON.parse our encoded JSON does indeed return the original input value

 // we really only care that JSON.parse can work with our result // the output value should match the input value // if it doesn't, we did something wrong in our stringifier const test = data => { return console.log(JSON.parse(stringifyJSON(data))) } test([1,2,3]) // should return [1,2,3] test({a:[1,2,3]}) // should return {a:[1,2,3]} 

Disclaimer: It should be obvious that the code I'm going to use is not intended to be used as an actual replacement for JSON.stringify - there are probably countless cases where we probably did not address. Instead, this code is used to provide a demonstration of how we could deal with such a task. Additional corner cases can be added to this function.




Runnable demo

Without further ado, here is stringifyJSON in a runnable demo that checks for superior compatibility for a few common cases

 const stringifyJSON = data => { if (data === undefined) return undefined else if (data === null) return 'null' else if (data.constructor === String) return '"' + data.replace(/"/g, '\\"') + '"' else if (data.constructor === Number) return String(data) else if (data.constructor === Boolean) return data ? 'true' : 'false' else if (data.constructor === Array) return '[ ' + data.reduce((acc, v) => { if (v === undefined) return [...acc, 'null'] else return [...acc, stringifyJSON(v)] }, []).join(', ') + ' ]' else if (data.constructor === Object) return '{ ' + Object.keys(data).reduce((acc, k) => { if (data[k] === undefined) return acc else return [...acc, stringifyJSON(k) + ':' + stringifyJSON(data[k])] }, []).join(', ') + ' }' else return '{}' } // round-trip test and log to console const test = data => { return console.log(JSON.parse(stringifyJSON(data))) } test(null) // null test('he said "hello"') // 'he said "hello"' test(5) // 5 test([1,2,true,false]) // [ 1, 2, true, false ] test({a:1, b:2}) // { a: 1, b: 2 } test([{a:1},{b:2},{c:3}]) // [ { a: 1 }, { b: 2 }, { c: 3 } ] test({a:[1,2,3], c:[4,5,6]}) // { a: [ 1, 2, 3 ], c: [ 4, 5, 6 ] } test({a:undefined, b:2}) // { b: 2 } test([[["test","mike",4,["jake"]],3,4]]) // [ [ [ 'test', 'mike', 4, [ 'jake' ] ], 3, 4 ] ] 



"So why is it better?"

  • this works not only for array types - we can create strings, numbers, arrays, objects, arrays of numbers, arrays of objects, objects containing arrays of strings, even null and undefined s, etc. - you get the idea
  • each instance of our stringifyJSON object stringifyJSON like a small program that tells us how to encode each type (e.g. String , Number , Array , Object , etc.)
  • no whacked out typeof type checking - after checking the cases of undefined and null we know that we can try to read the constructor property.
  • there is no manual cycle where we must mentally track counter variables, how / when to increase them
  • no complex if conditions using && , || ! or checking things like x > y.length , etc.
  • do not use obj[0] or obj[i] , which emphasizes our brain
  • no assumptions that arrays / objects are empty - and without checking the length property
  • no other mutations in this regard - this means that we do not need to think about any value of the returned master resarray or what state it causes after push calls at different stages of the program



Custom objects

JSON.stringify allows us to set the toJSON property on our custom objects, so that when we format them, we get the desired result.

 const Foo = x => ({ toJSON: () => ({ type: 'Foo', value: x }) }) console.log(JSON.stringify(Foo(5))) // {"type":"Foo","value":5} 

We could easily add this functionality to our code above - changes to bold

 const stringifyJSON = data => { if (data === undefined) return undefined else if (data === null) return 'null' else if (data.toJSON instanceof Function) return stringifyJSON(data.toJSON()) ... else return '{}' } test({toJSON: () => ({a:1, b:2})}) // { a: 1, b: 2 } 
+2
Mar 09 '17 at 8:22
source share

So, after playing with your code, I found that if you replace:

resarray.push('[' + obj[i] + ']');

from:

resarray.push(stringifyJSON(obj[i])) it works well with arrays and still satisfies your recursive process.

I also found several quirks with running it through objects such as { hello: 5, arr: testarray, arr2: [1, 2,3, "hello"], num: 789, str: '4444', str2: "67494" }; , and found that by changing the structure of objects:

stringifyJSON(resarray.push(key + '"' + ':' + obj[key]),resarray)

to something more similar:

stringifyJSON(resarray.push('"' + key + '"' + ':' + stringifyJSON(obj[key])),resarray);

he should train as much as you want. This is really great, although it was fun to play with him!

0
Mar 09 '17 at 6:39
source share



All Articles