2014-12-31 2 views
0

Я написал следующую функцию, чтобы пересечь JSON-структурированные данные, чтобы найти значение предоставленного targetField и путей pathArr, но я борюсь с возвратом результата из рекурсии, потому что значение для targetField может не существовать.JavaScript: результат возврата из рекурсии

Эта функция использует глобальную переменную с именем результат для возвращения значения в Таргет-филд, но это может увлекайтесь к следующему вызову findValue(), который не может найти значение, но показывает последний звонок-х result. Поэтому мне интересно, что лучше всего решить эту проблему, не используя глобальную переменную result.

Если вызов findValue() не находит значение targetField, просто верните null в качестве результата.

pathArr что-то вроде ["first_level", "second_level", "third_level"];

var result = null; 
function findValue(jsonData, pathArr, targetField) { 

if (pathArr.length == 0) { 
    result = targetField in jsonData ? jsonData[targetField] : result; 
    return result; 
} else { 

    var curNode = pathArr.shift(); 

    if (curNode in jsonData) { 
     jsonData = jsonData[curNode]; 
    } 

    if (Array.isArray(jsonData)) { 
     jsonData.forEach(function(thisData) { 
      findValue(thisData, pathArr, targetField); 
     }); 
    } else { 
     findValue(jsonData, pathArr, targetField); 
    } 
} 

return result; 
} 

Edit: Спасибо всем, кто ответил.

Используя усовершенствованный код unobf, я использовал этот Testdata сделать тестирование, но это только кажется, возвращая последний матч:

var testData = { 
"num_found" : 3, 
"category" : "social", 

"groups": [ 
{"group" : { 
    "source" : [{"id" : "testID1", "num": 10, "field": "sociaology", "sub-subject" : "socialeconomy"}, 
       {"id" : "testID2", "num": 20, "field": "mathematics", "sub-subject": ""}, 
       {"id" : "testID3", "num": 7, "field": "biology", "sub-subject" : ""} 
       ], 
    "identifier" : "shelf-01-E-XW1" 
}}, 
{"group" : { 
    "source" : [{"id" : "testID4", "num": 50, "field": "sociaology2", "sub-subject" : ""}, 
       {"id" : "testID5", "num": 44, "field": "mathematics2", "sub-subject": ""}, 
       {"id" : "testID6", "num": 75, "field": "biology2", "sub-subject" : "european studies2"} 
       ], 
    "identifier" : "shelf-02-W-EW3" 
}}, 

{"group" : { 
    "source" : [{"id" : "testID7", "num": 59, "field": "sociaology3", "sub-subject" : "socialeconomy3"}, 
       {"id" : "testID8", "num": 47, "field": "mathematics3", "sub-subject": ""}, 
       {"id" : "testID9", "num": 76, "field": "biology3", "sub-subject" : "european studies3"} 
       ], 
    "identifier" : "shelf-03-W-GW5" 
}} 
] 
}; 

, и я обнаружил, что я должен был изменить эту строку кода

return {value : result}; 

к

return result; 

Тест:

for (var i = 0; i < testData.groups.length; i++) { 
    var value = findValue(testData.groups[i], ["group", "source"], "sub-subject"); 
    console.log("found value: " + value); 
} 
+1

Посмотрите поиск в ширину (http://en.wikipedia.org/wiki/Breadth-first_search), и поиск в глубину (HTTP: //en.wikipedia.org/wiki/Depth-first_search), есть алгоритмы, которые объясняют, как это кодировать. – Patrick

+0

Почему вы вызываете 'findValue' рекурсивно, не возвращая возвращаемого значения, вместо этого полагаясь на этот вызов, задавая глобальную переменную ('result')? –

+0

Я считаю, что глобальная переменная приводит к временному решению. Сначала я попытался сузить масштаб результата переменной внутри функции, но это не сработало, но позвольте мне попробовать еще раз. – TonyGW

ответ

1

По существу, все пути через вашу функцию должны возвращать значение. В настоящее время, есть путь, который не делает, тот путь, который принимает forEach:

jsonData.forEach(function(thisData) { 
    findValue(thisData, pathArr, targetField); 
}); 

Там, вы, вероятно, хотите some (так что вы можете остановиться, когда вы его найти), и переменная, которую можно установить на найдены значение, например:

var retval; 

и

jsonData.some(function(thisData) { 
    retVal = findValue(thisData, pathArr, targetField); 
    return !!retVal; // Stops the `some` loop, doesn't exit `findValue` 
}); 
if (retVal) { 
    return retVal; 
} 

Side Примечание: Вы не хотите, т o объявить result вне функции, в этом нет необходимости. Функция может быть полностью автономной. Вот версия, которая является автономным, например:

function findValue(jsonData, pathArr, targetField) { 
    var result = null; 

    if (pathArr.length == 0) { 
     result = targetField in jsonData ? jsonData[targetField] : null; 
    } else { 

     var curNode = pathArr.shift(); 

     if (curNode in jsonData) { 
      jsonData = jsonData[curNode]; 
     } 

     if (Array.isArray(jsonData)) { 
      jsonData.some(function(thisData) { 
       result = findValue(thisData, pathArr, targetField); 
       return !!result; // Stops the `some` loop, doesn't exit `findValue` 
      }); 
     } else { 
      result = findValue(jsonData, pathArr, targetField); 
     } 
    } 

    return result; 
} 

Примечание: Я не делал полный обзор кода, просто смотрел на возвращаемые значения.

+0

спасибо, я пытаюсь сделать функцию полностью автономной. Использование глобальной переменной для хранения возвращаемого значения вовсе не является хорошей идеей. – TonyGW

+0

Должен использовать Array.find() вместо функции retVal закрытия - см. Мое решение – unobf

+0

@unobf: 'Array # find' не лучше и не хуже (за исключением того, что требуется полиполк), чем с помощью' some'. –

1

Вот решение с некоторыми тестовыми данными

function findValue(jsonData, pathArr, targetField) { 
    var result = null; 

    if (pathArr.length == 0 && jsonData.hasOwnProperty(targetField)) { 
     result = targetField in jsonData ? jsonData[targetField] : result; 
     return { value : result}; 
    } else { 
     jsonData = jsonData[pathArr.shift()] 
     if (Array.isArray(jsonData)) { 
      jsonData.forEach(function(thisData) { 
       result = findValue(thisData, pathArr, targetField); 
      }); 
     } else if (typeof jsonData !== 'undefined') { 
      return findValue(jsonData, pathArr, targetField); 
     } 
    } 
    return result; 
} 

testData = [{ 
    hello : 'hello', 
    pathArr: [] 
}, { 
    child : { 
     hello : false 
    }, 
    pathArr: ['child'] 
}, { 
    child : { 
     hello : null 
    }, 
    pathArr: ['child'] 
}, { 
    child: { 
     child: { 
      hell: 'hell' 
     } 
    }, 
    pathArr: ['child', 'child'] 
}]; 



testData.forEach(function (json) { 
    console.log(findValue(json, json.pathArr, 'hello')); 
}); 
+0

Примечание: 'Array # find' является функцией ES6, отсутствующей во многих современных браузерах. Если вы полагаетесь на него, вам понадобится полипол. –

+0

какой двойной! имею в виду? спасибо – TonyGW

+0

Вопрос не указывает, требуется ли решение только для браузера. Это будет работать в современных браузерах и Node.js – unobf

Смежные вопросы