Javascript 递归JSON.stringify实现
我正在努力学习Javascript中的递归,所以我想我应该重写本机的Javascript 递归JSON.stringify实现,javascript,recursion,Javascript,Recursion,我正在努力学习Javascript中的递归,所以我想我应该重写本机的JSON.stringify函数,使用递归来挑战自己。我的代码几乎可以正常工作了: var my_stringify = function(obj){ value = obj[ Object.keys(obj)[0] ]; index = Object.keys(obj)[0]; delete obj[ Object.keys(obj)[0] ]; // The value is just a
JSON.stringify
函数,使用递归来挑战自己。我的代码几乎可以正常工作了:
var my_stringify = function(obj){
value = obj[ Object.keys(obj)[0] ];
index = Object.keys(obj)[0];
delete obj[ Object.keys(obj)[0] ];
// The value is just a simple string, not a nested object
if (typeof value === 'string'){
if (Object.keys(obj).length !== 0){
// Continue recursion ..
return '"' + index + '":"' + value + '",' + my_stringify(obj);
}
// This would be the base case with a string at the end. Stop recursion.
return '"' + index + '":"' + value + '"}';
}
// The value is actually a nested object
else{
if (Object.keys(obj).length !== 0){
// Continue recursion ..
return '"' + index + '":{' + my_stringify(value) + ',' + my_stringify(obj);
}
// This is the base case with a nested object at the end. Stringify it and end recursion.
return '"' + index + '":{' + my_stringify(value) + '}';
}
}
除了我的答案中的第一个{
不见了之外,我不知道如何修复这个bug
例如,my_stringify({foo:'bar'})
返回“foo”:“bar”}
而不是{foo:“bar”}
另外,我知道我正在完全销毁原始对象,是否有任何方法可以在不删除任何内容的情况下(如obj.slice(1)
)向递归发送原始对象的简化版本
任何建议都将不胜感激 您需要将递归视为深入对象而不实际更改对象。看起来您正试图使用递归在对象内部横向移动 我已经编写了一个stringify版本,它处理基本对象(没有数组或函数) 这是你的电话号码 代码如下:
var my_stringify2 = function (obj) {
var objKeys = Object.keys(obj);
var keyValueArray = new Array();
for (var i = 0; i < objKeys.length; i++) {
var keyValueString = '"' + objKeys[i] + '":';
var objValue = obj[objKeys[i]];
keyValueString = (typeof objValue == "string") ?
keyValueString = keyValueString + '"' + objValue + '"' :
keyValueString = keyValueString + my_stringify2(objValue);
keyValueArray.push(keyValueString);
}
return "{" + keyValueArray.join(",") + "}";
}
结果是正确的json
{"foo":"bar","bar":"foo","foobar":{"foo":"bar","bar":"foo"}}
如果希望完全避免for循环,可以执行以下操作
在本例中,您像普通一样传递对象,但递归地传递一个键数组,从每个属性的键数组中移除一个元素
有点复杂,所以我添加了注释
var my_stringify2 = function (obj, objKeys) {
var str = "";
// keys haven't been loaded, either first pass, or processing a value of type object
if (objKeys == undefined) {
objKeys = Object.keys(obj);
str = "{"
} else {
// if keys array exists and is empty, no more properties to evaluate, return the end bracket
if (objKeys.length == 0) {
return "}";
// array exists and isn't empty, that means it's a property and not the first property, add a comma
} else {
str = ",";
}
}
// add the property name
str += '"' + objKeys[0] + '":';
// get the value
var objValue = obj[objKeys[0]];
// if the value type is string, add the string, if it's an object, call this function again, but leave the objKeys undefined
str +=
(typeof objValue == "string") ?
'"' + objValue + '"' :
my_stringify2(objValue);
// remove the first element fromt the keys array
objKeys.splice(0,1);
//call the function for the next property
return str + my_stringify2(obj, objKeys);
}
从根本上说,您是通过切断第一个属性进行字符串化,将其字符串化,然后递归对象的其余部分。不管怎么说,递归的唯一原因是当存在嵌套对象时,否则您应该只遍历属性。这样做,您就很难判断您是否位于对象的开头,并且应该用字符串返回缺少的
{
在半伪代码中(留给您一些自己做的工作),您需要这样的东西:
var my_stringify = function(obj) {
// check first for null / undefined / etc and return
var myJSON = "{";
// iterate through all the properties of the object
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
// check to see if this property is a string, number, etc
if (//...) {
myJSON += // the JSON representation of this value using p and obj[p]
}
if (// test for nested object) {
myJSON += my_stringify(obj[p]); // this is recursion!
}
if (// test for arrays) {
// arrays also need special handling and note that they might
// include objects or other arrays - more chances for recursion!
}
// note: functions should be ignored, they aren't included in JSON
}
}
return myJSON + "}";
}
我不同意@Bergi的说法,即常规的旧递归不适合这种情况。正如我在评论中所说,通过将索引作为参数传递给函数,可以避免使用
for
循环。这是一种非常常见的技术,可以防止您需要复制或修改数据结构
下面是我对这种实现的尝试。正如您所看到的,它非常简单(令我自己惊讶的是,它可以工作!):
函数jsonify(obj,idx){
var json,objStr=toString.call(obj);
//处理字符串
如果(objStr=='[对象字符串]'){return'+obj+'''}
idx=idx | | 0
//句柄数组
if(objStr=='[object Array]'){
如果(idx>=对象长度){
//下面的代码确保我们永远不会超过数组的末尾,
//所以我们可以假设这是一个空数组
返回“[]”
}
//JSONify在idx处的值
json=jsonify(obj[idx])
if(idx有很多方法可以加强这一点(我肯定也有一些漏洞),但你知道了。我在采访中被问到这个问题,这就是我想到的。 可理解的递归方法:
函数字符串化(输入){
var-arrVals=[];
Object.keys(输入).forEach(函数(键名){
设val=input[keyName];
if(typeof val!=“未定义”&&typeof val!=“函数”){
arrVals.push(getQuotedString(键名)+“:”+getString(val));
}
});
返回'{'+arrVals.join(',')+'}';
}
函数getString(val){
开关(val类型){
大小写“string”:
返回getQuotedString(val);
打破
案件编号:
“布尔”大小写:
返回val;
打破
案例“对象”:
如果(val==null){
返回“null”;
}
if(数组isArray(val)){
让arrString=[]
for(设i=0;i }
旧问题的新答案
这里有一些非常糟糕的答案,即使在最简单的例子下也会失败。这个答案旨在详尽地回答这个问题,并演示这样的方法在处理各种数据类型和
角落案例
此函数对一个非空数据的<代码>构造函数< /代码>属性进行简单的实例分析,并相应地编码。它管理了许多不可能考虑的角情况,例如
。
返回JSON.stringify(未定义)
undefined
返回JSON.stringify(null)
'null'
返回JSON.stringify(true)
'true'
返回JSON.stringify([1,2,未定义,4])
'[1,2,null,4]'
var my_stringify = function(obj) { // check first for null / undefined / etc and return var myJSON = "{"; // iterate through all the properties of the object for (var p in obj) { if (obj.hasOwnProperty(p)) { // check to see if this property is a string, number, etc if (//...) { myJSON += // the JSON representation of this value using p and obj[p] } if (// test for nested object) { myJSON += my_stringify(obj[p]); // this is recursion! } if (// test for arrays) { // arrays also need special handling and note that they might // include objects or other arrays - more chances for recursion! } // note: functions should be ignored, they aren't included in JSON } } return myJSON + "}"; }
function jsonify(obj, idx) { var json, objStr = toString.call(obj); // Handle strings if(objStr == '[object String]') { return '"' + obj + '"' } idx = idx || 0 // Handle arrays if(objStr == '[object Array]') { if(idx >= obj.length) { // The code below ensures we'll never go past the end of the array, // so we can assume this is an empty array return "[]" } // JSONify the value at idx json = jsonify( obj[idx] ) if(idx < obj.length - 1) { // There are items left in the array, so increment the index and // JSONify the rest json = json + "," + jsonify( obj, idx + 1 ) } // If this is the first item in the array, wrap the result in brackets if(idx === 0) { return "[" + json + "]" } return json } // Handle objects if(obj === Object(obj)) { var keys = Object.keys(obj) var key = keys[idx] // JSONify the key and value json = '"' + key + '":' + jsonify( obj[key] ) if(idx < keys.length - 1) { // There are more keys, so increment the index and JSONify the rest return json + "," + jsonify( obj, idx + 1 ) } // If this is the first key, wrap the result in curly braces if(idx === 0) { return "{" + json + "}" } return json } return obj.toString() // Naively handle everything else } var items = [ 9, "nine", { "key": [], "key2": { "subkey": 3.333 } } ] console.log("OUTPUT", jsonify(items)) // => OUTPUT [9,"nine","key":[],"key2":{"subkey":3.333}]
// 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]}