Проверка наличия вложенного объекта объекта JavaScript
Если у меня есть ссылка на объект:
var test = {};
потенциально (но не сразу) будет иметь вложенные объекты, что-то вроде:
{level1: {level2: {level3: "level3"}}};
Как лучше всего проверить наличие свойства в глубоко вложенных объектах?
alert(test.level1);
дает undefined
, но alert(test.level1.level2.level3);
выходит из строя.
В настоящее время я делаю что-то вроде этого:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
но мне было интересно, если есть лучший способ.
Ответы
Ответ 1
Вы должны делать это шаг за шагом, если вы не хотите TypeError
, потому что, если один из участников - null
или undefined
, и вы пытаетесь получить доступ к члену, будет выдано исключение.
Вы можете либо просто catch
исключение, либо создать функцию для проверки существования нескольких уровней, примерно так:
function checkNested(obj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < args.length; i++) {
if (!obj || !obj.hasOwnProperty(args[i])) {
return false;
}
obj = obj[args[i]];
}
return true;
}
var test = {level1:{level2:{level3:'level3'}} };
checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false
ОБНОВЛЕНИЕ 2019-05-16:
Вот более короткая версия, использующая функции и рекурсию ES6 (она также находится в правильной форме хвостового вызова):
function checkNested(obj, level, ...rest) {
if (obj === undefined) return false
if (rest.length == 0 && obj.hasOwnProperty(level)) return true
return checkNested(obj[level], ...rest)
}
ОБНОВЛЕНИЕ 2019-10-17:
Необязательное предложение о связывании достигло этапа 3 в процессе комитета ECMAScript, это позволит вам безопасно получить доступ к глубоко вложенным свойствам с помощью токена ?.
, нового необязательного оператора связывания:
const value = obj?.level1?.level2?.level3
Если какой-либо из уровней доступа - null
или undefined
, выражение будет преобразовано в undefined
.
Предложение также позволяет безопасно обрабатывать вызовы методов:
obj?.level1?.method();
Приведенное выше выражение будет выдавать undefined
, если obj
, obj.level1
или obj.level1.method
равны null
или undefined
, в противном случае оно вызовет функцию.
Вы можете начать играть с этой функцией с помощью Babel, используя дополнительный плагин цепочки.
Посмотрите этот пример на Babel REPL.
Ответ 2
Вот образец я взял из Оливера Стила:
var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );
На самом деле, вся статья - это обсуждение того, как вы можете это сделать в javascript. Он полагается на использование вышеприведенного синтаксиса (который не так сложно прочитать, как только вы привыкнете к нему) в качестве идиомы.
Ответ 3
Update
Похоже, lodash добавил _.get
для всех ваших потребностей в вложенных свойствах.
_.get(countries, 'greece.sparta.playwright')
https://lodash.com/docs#get
Предыдущий ответ
lodash пользователи могут наслаждаться lodash.contrib который имеет пары методов, которые смягчают эту проблему.
GetPath
Подпись: _.getPath(obj:Object, ks:String|Array)
Получает значение на любой глубине во вложенном объекте на основе пути, описанного в
заданные ключи. Ключи могут быть заданы как массив или как строка, разделенная точками.
Возвращает undefined
, если путь невозможен.
var countries = {
greece: {
athens: {
playwright: "Sophocles"
}
}
}
};
_.getPath(countries, "greece.athens.playwright");
// => "Sophocles"
_.getPath(countries, "greece.sparta.playwright");
// => undefined
_.getPath(countries, ["greece", "athens", "playwright"]);
// => "Sophocles"
_.getPath(countries, ["greece", "sparta", "playwright"]);
// => undefined
Ответ 4
Я сделал тесты производительности (спасибо cdMinix за добавление lodash ) по некоторым предложениям, предложенным по этому вопросу, с приведенными ниже результатами.
Отказ от ответственности # 1 Включение строк в ссылки - это ненужное метапрограммирование и, вероятно, лучше всего избежать. Не теряйте информацию о своих ссылках. Читайте больше из этого ответа на аналогичный вопрос.
Отказ от ответственности # 2 Мы говорим о миллионах операций за миллисекунду здесь. Очень маловероятно, что любой из них имеет большое значение в большинстве случаев использования. Выберите то, что лучше всего знает ограничения каждого из них. Для меня я бы пошел с чем-то вроде reduce
из удобства.
Обертка объекта (Оливер Стил) - 34% - самый быстрый
var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;
Исходное решение (предлагается в вопросе) - 45%
var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;
checkNested - 50%
function checkNested(obj) {
for (var i = 1; i < arguments.length; i++) {
if (!obj.hasOwnProperty(arguments[i])) {
return false;
}
obj = obj[arguments[i]];
}
return true;
}
get_if_exist - 52%
function get_if_exist(str) {
try { return eval(str) }
catch(e) { return undefined }
}
validChain - 54%
function validChain( object, ...keys ) {
return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
objHasKeys - 63%
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
nestedPropertyExists - 69%
function nestedPropertyExists(obj, props) {
var prop = props.shift();
return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}
_. get - 72%
deeptest - 86%
function deeptest(target, s){
s= s.split('.')
var obj= target[s.shift()];
while(obj && s.length) obj= obj[s.shift()];
return obj;
}
печальные клоуны - 100% - самый медленный
var o = function(obj) { return obj || {} };
var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);
Ответ 5
Вы можете прочитать свойство объекта на любой глубине, если вы обрабатываете имя как строку: 't.level1.level2.level3'
.
window.t={level1:{level2:{level3: 'level3'}}};
function deeptest(s){
s= s.split('.')
var obj= window[s.shift()];
while(obj && s.length) obj= obj[s.shift()];
return obj;
}
alert(deeptest('t.level1.level2.level3') || 'Undefined');
Он возвращает undefined
, если любой из сегментов undefined
.
Ответ 6
var a;
a = {
b: {
c: 'd'
}
};
function isset (fn) {
var value;
try {
value = fn();
} catch (e) {
value = undefined;
} finally {
return value !== undefined;
}
};
// ES5
console.log(
isset(function () { return a.b.c; }),
isset(function () { return a.b.c.d.e.f; })
);
Если вы кодируете среду ES6 (или используя 6to5), вы можете воспользоваться функция arrow:
// ES6 using the arrow function
console.log(
isset(() => a.b.c),
isset(() => a.b.c.d.e.f)
);
Что касается производительности, для использования блока try..catch
не требуется штраф за производительность, если установлено свойство. Эффект влияет на производительность, если свойство не установлено.
Подумайте, просто используя _.has
:
var object = { 'a': { 'b': { 'c': 3 } } };
_.has(object, 'a');
// → true
_.has(object, 'a.b.c');
// → true
_.has(object, ['a', 'b', 'c']);
// → true
Ответ 7
как насчет
try {
alert(test.level1.level2.level3)
} catch(e) {
...whatever
}
Ответ 8
Ответ ES6, тщательно протестирован :)
const propExists = (obj, path) => {
return !!path.split('.').reduce((obj, prop) => {
return obj && obj[prop] ? obj[prop] : undefined;
}, obj)
}
Ответ 9
Вы также можете использовать дополнительное предложение о связывании tc39 вместе с babel 7 - tc39-offer-option-chaining
Код будет выглядеть так:
const test = test?.level1?.level2?.level3;
if (test) alert(test);
Ответ 10
Я попробовал рекурсивный подход:
function objHasKeys(obj, keys) {
var next = keys.shift();
return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}
! keys.length ||
выходит из рекурсии, поэтому он не запускает функцию без клавиш, оставшихся для проверки. Тесты:
obj = {
path: {
to: {
the: {
goodKey: "hello"
}
}
}
}
console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey'])); // undefined
Я использую его для печати дружественного html-представления кучи объектов с неизвестным ключом/значениями, например:
var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
? myObj.MachineInfo.BiosInfo.Name
: 'unknown';
Ответ 11
Я думаю, что следующее script дает более читаемое представление.
объявить функцию:
var o = function(obj) { return obj || {};};
то используйте его следующим образом:
if (o(o(o(o(test).level1).level2).level3)
{
}
Я называю это "грубой техникой клоуна", потому что он использует знак o (
EDIT:
вот версия для TypeScript
он дает проверки типов во время компиляции (а также intellisense, если вы используете такой инструмент, как Visual Studio)
export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
if (typeof someObject === 'undefined' || someObject === null)
return defaultValue;
else
return someObject;
}
использование одного и того же:
o(o(o(o(test).level1).level2).level3
но на этот раз intellisense работает!
плюс, вы можете установить значение по умолчанию:
o(o(o(o(o(test).level1).level2).level3, "none")
Ответ 12
Один простой способ:
try {
alert(test.level1.level2.level3);
} catch(e) {
alert("undefined"); // this is optional to put any output here
}
try/catch
обнаруживает случаи, когда не определены какие-либо объекты более высокого уровня, такие как test, test.level1, test.level1.level2.
Ответ 13
Я не видел никого, кто использовал Proxies
Поэтому я придумал свое. Самое замечательное в том, что вам не нужно интерполировать строки. Фактически вы можете вернуть функцию объекта, пригодную для цепочки, и сделать с ней какие-то магические вещи. Вы можете даже вызывать функции и получать индексы массивов для проверки глубинных объектов
function resolve(target) {
var noop = () => {} // We us a noop function so we can call methods also
return new Proxy(noop, {
get(noop, key) {
// return end result if key is _result
return key === '_result'
? target
: resolve( // resolve with target value or undefined
target === undefined ? undefined : target[key]
)
},
// if we want to test a function then we can do so alos thanks to using noop
// instead of using target in our proxy
apply(noop, that, args) {
return resolve(typeof target === 'function' ? target.apply(that, args) : undefined)
},
})
}
// some modified examples from the accepted answer
var test = {level1: {level2:() => ({level3:'level3'})}}
var test1 = {key1: {key2: ['item0']}}
// You need to get _result in the end to get the final result
console.log(resolve(test).level1.level2().level3._result)
console.log(resolve(test).level1.level2().level3.level4.level5._result)
console.log(resolve(test1).key1.key2[0]._result)
console.log(resolve(test1)[0].key._result) // don't exist
Ответ 14
Основываясь на этом ответе, я придумал эту общую функцию, используя ES2015
, которая решила бы проблему
function validChain( object, ...keys ) {
return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}
var test = {
first: {
second: {
third: "This is not the key your are looking for"
}
}
}
if ( validChain( test, "first", "second", "third" ) ) {
console.log( test.first.second.third );
}
Ответ 15
Я создал небольшую функцию для безопасного получения вложенных свойств объекта.
function getValue(object, path, fallback, fallbackOnFalsy) {
if (!object || !path) {
return fallback;
}
// Reduces object properties to the deepest property in the path argument.
return path.split('.').reduce((object, property) => {
if (object && typeof object !== 'string' && object.hasOwnProperty(property)) {
// The property is found but it may be falsy.
// If fallback is active for falsy values, the fallback is returned, otherwise the property value.
return !object[property] && fallbackOnFalsy ? fallback : object[property];
} else {
// Returns the fallback if current chain link does not exist or it does not contain the property.
return fallback;
}
}, object);
}
Или более простая, но немного нечитаемая версия:
function getValue(o, path, fb, fbFalsy) {
if(!o || !path) return fb;
return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? !o[p] && fbFalsy ? fb : o[p] : fb, o);
}
Или даже короче, но без отступления от ложного флага:
function getValue(o, path, fb) {
if(!o || !path) return fb;
return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? o[p] : fb, o);
}
У меня тест с:
const obj = {
c: {
a: 2,
b: {
c: [1, 2, 3, {a: 15, b: 10}, 15]
},
c: undefined,
d: null
},
d: ''
}
А вот несколько тестов:
// null
console.log(getValue(obj, 'c.d', 'fallback'));
// array
console.log(getValue(obj, 'c.b.c', 'fallback'));
// array index 2
console.log(getValue(obj, 'c.b.c.2', 'fallback'));
// no index => fallback
console.log(getValue(obj, 'c.b.c.10', 'fallback'));
Чтобы увидеть весь код с документацией и тестами, которые я пробовал, вы можете проверить мой github gist:https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js
Ответ 16
Более короткая версия ES5 версии @CMS отличного ответа:
// Check the obj has the keys in the order mentioned. Used for checking JSON results.
var checkObjHasKeys = function(obj, keys) {
var success = true;
keys.forEach( function(key) {
if ( ! obj.hasOwnProperty(key)) {
success = false;
}
obj = obj[key];
})
return success;
}
С аналогичным тестом:
var test = { level1:{level2:{level3:'result'}}};
utils.checkObjHasKeys(test, ['level1', 'level2', 'level3']); // true
utils.checkObjHasKeys(test, ['level1', 'level2', 'foo']); // false
Ответ 17
Ответ, предоставленный CMS, отлично работает со следующей модификацией для нулевых проверок.
function checkNested(obj /*, level1, level2, ... levelN*/)
{
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
for (var i = 0; i < args.length; i++)
{
if (obj == null || !obj.hasOwnProperty(args[i]) )
{
return false;
}
obj = obj[args[i]];
}
return true;
}
Ответ 18
Следующие параметры были разработаны начиная с этого ответа. Это же дерево для обоих:
var o = { a: { b: { c: 1 } } };
Остановить поиск, если undefined
var u = undefined;
o.a ? o.a.b ? o.a.b.c : u : u // 1
o.x ? o.x.y ? o.x.y.z : u : u // undefined
(o = o.a) ? (o = o.b) ? o.c : u : u // 1
Обеспечьте каждый уровень один за другим
var $ = function (empty) {
return function (node) {
return node || empty;
};
}({});
$($(o.a).b).c // 1
$($(o.x).y).z // undefined
Ответ 19
Я знаю, что этот вопрос старый, но я хотел предложить расширение, добавив его ко всем объектам. Я знаю, что люди склонны хмуриться от использования прототипа Object для расширенных функциональных возможностей объекта, но я не нахожу ничего проще, чем делать это. Кроме того, теперь это разрешено с помощью метода Object.defineProperty.
Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
for( var i = 0; i<needles.length; i++ ) {
if( !obj.hasOwnProperty(needles[i])) {
return false;
}
obj = obj[needles[i]];
}
return true;
}});
Теперь, чтобы проверить какое-либо свойство в любом объекте, вы можете просто сделать:
if( obj.has("some.deep.nested.object.somewhere") )
Здесь jsfiddle, чтобы проверить его, и в частности он включает в себя некоторый jQuery, который прерывается, если вы изменяете Object.prototype напрямую из-за свойство становится перечислимым. Это должно хорошо работать с сторонними библиотеками.
Ответ 20
Я думаю, что это небольшое улучшение (становится 1-лайнером):
alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )
Это работает, потому что && оператор возвращает конечный операнд, который он оценил (и это короткое замыкание).
Ответ 21
Я искал возвращаемое значение, если свойство существует, поэтому я изменил ответ на CMS выше. Вот что я придумал:
function getNestedProperty(obj, key) {
// Get property array from key string
var properties = key.split(".");
// Iterate through properties, returning undefined if object is null or property doesn't exist
for (var i = 0; i < properties.length; i++) {
if (!obj || !obj.hasOwnProperty(properties[i])) {
return;
}
obj = obj[properties[i]];
}
// Nested property found, so return the value
return obj;
}
Usage:
getNestedProperty(test, "level1.level2.level3") // "level3"
getNestedProperty(test, "level1.level2.foo") // undefined
Ответ 22
Здесь я беру на себя это - большинство из этих решений игнорируют случай вложенного массива, как в:
obj = {
"l1":"something",
"l2":[{k:0},{k:1}],
"l3":{
"subL":"hello"
}
}
Я могу проверить на obj.l2[0].k
С помощью функции ниже вы можете сделать deeptest('l2[0].k',obj)
Функция вернет true, если объект существует, иначе false
function deeptest(keyPath, testObj) {
var obj;
keyPath = keyPath.split('.')
var cKey = keyPath.shift();
function get(pObj, pKey) {
var bracketStart, bracketEnd, o;
bracketStart = pKey.indexOf("[");
if (bracketStart > -1) { //check for nested arrays
bracketEnd = pKey.indexOf("]");
var arrIndex = pKey.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
pKey = pKey.substr(0, bracketStart);
var n = pObj[pKey];
o = n? n[arrIndex] : undefined;
} else {
o = pObj[pKey];
}
return o;
}
obj = get(testObj, cKey);
while (obj && keyPath.length) {
obj = get(obj, keyPath.shift());
}
return typeof(obj) !== 'undefined';
}
var obj = {
"l1":"level1",
"arr1":[
{"k":0},
{"k":1},
{"k":2}
],
"sub": {
"a":"letter A",
"b":"letter B"
}
};
console.log("l1: " + deeptest("l1",obj));
console.log("arr1[0]: " + deeptest("arr1[0]",obj));
console.log("arr1[1].k: " + deeptest("arr1[1].k",obj));
console.log("arr1[1].j: " + deeptest("arr1[1].j",obj));
console.log("arr1[3]: " + deeptest("arr1[3]",obj));
console.log("arr2: " + deeptest("arr2",obj));
Ответ 23
Это работает со всеми объектами и массивами:)
ex:
if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
//do something
}
это моя улучшенная версия ответа Брайана
Я использовал _has как имя свойства, потому что он может конфликтовать с существующим свойством (например: maps)
Object.defineProperty( Object.prototype, "_has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
var needles_full=[];
var needles_square;
for( var i = 0; i<needles.length; i++ ) {
needles_square = needles[i].split( "[" );
if(needles_square.length>1){
for( var j = 0; j<needles_square.length; j++ ) {
if(needles_square[j].length){
needles_full.push(needles_square[j]);
}
}
}else{
needles_full.push(needles[i]);
}
}
for( var i = 0; i<needles_full.length; i++ ) {
var res = needles_full[i].match(/^((\d+)|"(.+)"|'(.+)')\]$/);
if (res != null) {
for (var j = 0; j < res.length; j++) {
if (res[j] != undefined) {
needles_full[i] = res[j];
}
}
}
if( typeof obj[needles_full[i]]=='undefined') {
return false;
}
obj = obj[needles_full[i]];
}
return true;
}});
Здесь fiddle
Ответ 24
Теперь мы можем также использовать reduce
для циклического ввода вложенных ключей:
// @params o<object>
// @params path<string> expects 'obj.prop1.prop2.prop3'
// returns: obj[path] value or 'false' if prop doesn't exist
const objPropIfExists = o => path => {
const levels = path.split('.');
const res = (levels.length > 0)
? levels.reduce((a, c) => a[c] || 0, o)
: o[path];
return (!!res) ? res : false
}
const obj = {
name: 'Name',
sys: { country: 'AU' },
main: { temp: '34', temp_min: '13' },
visibility: '35%'
}
const exists = objPropIfExists(obj)('main.temp')
const doesntExist = objPropIfExists(obj)('main.temp.foo.bar.baz')
console.log(exists, doesntExist)
Ответ 25
Вы можете сделать это, используя рекурсивную функцию. Это будет работать, даже если вы не знаете все вложенные имена ключей объектов.
function FetchKeys(obj) {
let objKeys = [];
let keyValues = Object.entries(obj);
for (let i in keyValues) {
objKeys.push(keyValues[i][0]);
if (typeof keyValues[i][1] == "object") {
var keys = FetchKeys(keyValues[i][1])
objKeys = objKeys.concat(keys);
}
}
return objKeys;
}
let test = { level1: { level2: { level3: "level3" } } };
let keyToCheck = "level2";
let keys = FetchKeys(test); //Will return an array of Keys
if (keys.indexOf(keyToCheck) != -1) {
//Key Exists logic;
}
else {
//Key Not Found logic;
}
Ответ 26
theres функция здесь, на кодекоде (safeRead), который сделает это безопасным образом... i.e.
safeRead(test, 'level1', 'level2', 'level3');
если любое свойство равно null или undefined, возвращается пустая строка
Ответ 27
Основываясь на предыдущем комментарии, вот еще одна версия, где главный объект также не может быть определен:
// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;
Ответ 28
Я написал свою собственную функцию, которая принимает желаемый путь и имеет хорошую и плохую функцию обратного вызова.
function checkForPathInObject(object, path, callbackGood, callbackBad){
var pathParts = path.split(".");
var currentObjectPath = object;
// Test every step to see if it exists in object
for(var i=0; i<(pathParts.length); i++){
var currentPathPart = pathParts[i];
if(!currentObjectPath.hasOwnProperty(pathParts[i])){
if(callbackBad){
callbackBad();
}
return false;
} else {
currentObjectPath = currentObjectPath[pathParts[i]];
}
}
// call full path in callback
callbackGood();
}
Использование:
var testObject = {
level1:{
level2:{
level3:{
}
}
}
};
checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good
checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad
Ответ 29
//Just in case is not supported or not included by your framework
//***************************************************
Array.prototype.some = function(fn, thisObj) {
var scope = thisObj || window;
for ( var i=0, j=this.length; i < j; ++i ) {
if ( fn.call(scope, this[i], i, this) ) {
return true;
}
}
return false;
};
//****************************************************
function isSet (object, string) {
if (!object) return false;
var childs = string.split('.');
if (childs.length > 0 ) {
return !childs.some(function (item) {
if (item in object) {
object = object[item];
return false;
} else return true;
});
} else if (string in object) {
return true;
} else return false;
}
var object = {
data: {
item: {
sub_item: {
bla: {
here : {
iam: true
}
}
}
}
}
};
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'x')); // false
console.log(isSet(object,'data.sub_item')); // false
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'data.item.sub_item.bla.here.iam')); // true
Ответ 30
У меня была такая же проблема, и мне хотелось узнать, могу ли я придумать собственное решение. Это принимает путь, который вы хотите проверить как строку.
function checkPathForTruthy(obj, path) {
if (/\[[a-zA-Z_]/.test(path)) {
console.log("Cannot resolve variables in property accessors");
return false;
}
path = path.replace(/\[/g, ".");
path = path.replace(/]|'|"/g, "");
path = path.split(".");
var steps = 0;
var lastRef = obj;
var exists = path.every(key => {
var currentItem = lastRef[path[steps]];
if (currentItem) {
lastRef = currentItem;
steps++;
return true;
} else {
return false;
}
});
return exists;
}
Вот фрагмент с некоторыми протоколами и тестовыми примерами:
console.clear();
var testCases = [
["data.Messages[0].Code", true],
["data.Messages[1].Code", true],
["data.Messages[0]['Code']", true],
['data.Messages[0]["Code"]', true],
["data[Messages][0]['Code']", false],
["data['Messages'][0]['Code']", true]
];
var path = "data.Messages[0].Code";
var obj = {
data: {
Messages: [{
Code: "0"
}, {
Code: "1"
}]
}
}
function checkPathForTruthy(obj, path) {
if (/\[[a-zA-Z_]/.test(path)) {
console.log("Cannot resolve variables in property accessors");
return false;
}
path = path.replace(/\[/g, ".");
path = path.replace(/]|'|"/g, "");
path = path.split(".");
var steps = 0;
var lastRef = obj;
var logOutput = [];
var exists = path.every(key => {
var currentItem = lastRef[path[steps]];
if (currentItem) {
logOutput.push(currentItem);
lastRef = currentItem;
steps++;
return true;
} else {
return false;
}
});
console.log(exists, logOutput);
return exists;
}
testCases.forEach(testCase => {
if (checkPathForTruthy(obj, testCase[0]) === testCase[1]) {
console.log("Passed: " + testCase[0]);
} else {
console.log("Failed: " + testCase[0] + " expected " + testCase[1]);
}
});