【问题标题】:Recursive methods using Javascript使用 Javascript 的递归方法
【发布时间】:2017-07-29 23:51:20
【问题描述】:

我正在尝试复制 json stringify 方法,而是使用递归。我已经能够通过很多测试用例,但是当涉及到嵌套数组时,我似乎遇到了问题。如果数组('[]')中有任何空数组,我会得到类似 [,7,9] 而不是 [[],7,9] 的东西。另外,如果我通过:

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

我以为我已经接近完成这项工作了,但我可能需要重新开始。你们对我可以进行哪些更改以使其适用于嵌套示例有什么想法吗?这是我现在拥有的代码:

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's 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

【问题讨论】:

  • 我还没有完成代码,但是调用递归函数时需要返回吗?例如return stringifyJSON(...)
  • 这是一个尝试解决的有趣问题,但请确保它只是为了锻炼而不是您实际使用的东西——我在下面的回答中提供了许多细节。如果您有任何问题,我很乐意为您提供帮助。

标签: javascript arrays json recursion


【解决方案1】:

你们对我可以进行哪些更改以使其适用于嵌套示例有什么想法吗?

当然,但它会废弃你的整个功能,所以我希望你不介意。我将提供一个要点列表,说明为什么这种方法是必不可少的,而你的方法从一开始就有缺陷:(


角落案例

此函数对非空数据的constructor 属性进行简单的案例分析并进行相应的编码。它设法涵盖了许多您不太可能考虑的极端情况,例如

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

所以为了验证我们的stringifyJSON 函数是否确实正常工作,我不打算直接测试它的输出。相反,我将编写一个小的 test 方法来确保我们编码的 JSON 的 JSON.parse 实际上返回我们的原始输入值

// 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]}

免责声明:很明显,我将要分享的代码并不打算用作JSON.stringify 的实际替代品 - 有无数个角落我们可能没有解决的情况。相反,此代码被共享以提供我们如何执行此类任务的演示。可以轻松地将其他极端情况添加到此功能中。


可运行演示

事不宜迟,下面是stringifyJSON 在一个可运行的演示中,它验证了几种常见情况的出色兼容性

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 ] ]

“那为什么这样更好呢?”

  • 这不仅仅适用于数组类型——我们可以字符串化字符串、数字、数组、对象、数字数组、对象数组、包含字符串数组的对象,甚至nulls 和undefineds,等等开 - 你明白了
  • stringifyJSON 对象的每个 case 就像一个小程序,告诉我们如何准确地对每种类型进行编码(例如 StringNumberArrayObject 等)
  • 没有破坏 typeof 类型检查 - 在我们检查了 undefinednull 案例之后,我们知道我们可以尝试读取 constructor 属性。
  • 没有手动循环,我们必须在精神上跟踪计数器变量,如何/何时增加它们
  • 没有复杂的if条件使用&amp;&amp;||!,或检查x &gt; y.length等内容
  • 不要使用 obj[0]obj[i] 让我们的大脑紧张
  • 没有关于数组/对象为空的假设——并且无需检查length属性
  • 没有其他突变 - 这意味着我们不必考虑某些主返回值 resarray 或在程序的各个阶段发生 push 调用后它处于什么状态

自定义对象

JSON.stringify 允许我们在自定义对象上设置toJSON 属性,这样当我们对它们进行字符串化时,我们就会得到我们想要的结果。

const Foo = x => ({
  toJSON: () => ({ type: 'Foo', value: x })
})

console.log(JSON.stringify(Foo(5)))
// {"type":"Foo","value":5}

我们可以轻松地将这种功能添加到我们上面的代码中 - 粗体

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】:

    所以在玩弄了你的代码之后,我发现如果你替换:

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

    与:

    resarray.push(stringifyJSON(obj[i])) 它适用于数组并且仍然满足您的递归过程。

    我还发现了一些通过 { hello: 5, arr: testarray, arr2: [1, 2,3, "hello"], num: 789, str: '4444', str2: "67494" }; 之类的对象运行它的怪癖,并发现通过更改对象的字符串化来自:

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

    类似于:

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

    它应该锻炼更多你想要的方式。不过这真的很酷,我玩得很开心!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-03
      • 1970-01-01
      • 2016-08-05
      • 2021-01-31
      • 2016-04-10
      • 1970-01-01
      • 1970-01-01
      • 2016-04-01
      相关资源
      最近更新 更多