2016-11-25 2 views
0

Я использую Firebase, и их update функция позволяет обновлять многофилиальными, используя следующий синтаксис:Как получить ключевую ссылку для вложенного свойства в объекте?

newData['users/abc/age'] = 23 
newData['users/xyz/hair'] = 'blue' 
firebase.database().ref().update(newData) 

Я работаю на функцию полезности, которая будет сплющить глубоко вложенные объекты в объект как newData. Это включает в себя рекурсивно итерацию через объект и отслеживание ключей по мере того, как цикл спускается.

До сих пор у меня есть это (это просто добавив путь русов в массив в настоящее время после того, как алго правильно, я буду просто использовать .join('/').):

function getKeypaths(obj, paths = []) { 

    let keys = Object.keys(obj) 

    for (let i = 0; i < keys.length; i++) { 
     let propName = keys[i] 

     paths.push(propName) 
     if (typeof obj[propName] === "object" && typeof obj[propName] !== null) { 
      console.log("PUSHING PATH", propName, paths) 
      getKeypaths(obj[propName], paths) 
     } else { 
      console.log("val", obj[propName], paths) 

     } 
    } 
} 


let obj = { 
    a: { 
     b: { 
      c: { 
       d: 1 
      } 
     }, 
     x: { 
      y: { 
       z: 2 
      } 
     } 
    } 
} 

getKeypaths(obj) 

Когда я запускаю это, console.log Результат выглядит следующим образом:

PUSHING PATH a [ 'a' ] 
PUSHING PATH b [ 'a', 'b' ] 
PUSHING PATH c [ 'a', 'b', 'c' ] 
val 1 [ 'a', 'b', 'c', 'd' ] 
PUSHING PATH x [ 'a', 'b', 'c', 'd', 'x' ] 
PUSHING PATH y [ 'a', 'b', 'c', 'd', 'x', 'y' ] 
val 2 [ 'a', 'b', 'c', 'd', 'x', 'y', 'z' ] 

Как вы можете видеть, цикл сохранения пути предыдущего значения, когда он начинает рыть для следующего значения не-объекта. последняя строка результата следует читать: val 2 [ 'a', 'x', 'y', 'z' ]

Затем я попытался очистить path массив после того, как цикл возвращается к предыдущему уровню:

function getKeypaths(obj, paths = []) { 
    let keys = Object.keys(obj) 
    for (let i = 0; i < keys.length; i++) { 
     let propName = keys[i] 
     if (propName) paths.push(propName) 
     if (typeof obj[propName] === "object" && typeof obj[propName] !== null) { 
      console.log("PUSHING PATH", propName, paths) 
      getKeypaths(obj[propName], paths) 
      paths.length = 0 
     } else { 
      console.log("val", obj[propName], paths) 
     } 
    } 
} 

getKeypaths(obj) 

Но это привело лишь к петле забывая path до его уровень тока после первого value:

PUSHING PATH a [ 'a' ] 
PUSHING PATH b [ 'a', 'b' ] 
PUSHING PATH c [ 'a', 'b', 'c' ] 
val 1 [ 'a', 'b', 'c', 'd' ] 
PUSHING PATH x [ 'x' ] 
PUSHING PATH y [ 'x', 'y' ] 
val 2 [ 'x', 'y', 'z' ] 

Любые предложения? Откройте для вспомогательных библиотек, если есть более простой способ сделать это.

ответ

1

Вы должны уметь использовать json-pointer, чтобы делать то, что вы хотите.

Этот модуль включает в себя функцию dict, которая возвращает объект, содержащий ключи указателя JSON и связанные с ними значения. Например:

var jsonPointer = require("json-pointer"); 

var data = { 
    users: { 
     abc: { 
      age: 23 
     }, 
     xyz: { 
      hair: "blue" 
     } 
    } 
}; 
var dict = jsonPointer.dict(data); 

Полученный словарь будет:

{ 
    "https://stackoverflow.com/users/abc/age": 23, 
    "https://stackoverflow.com/users/xyz/hair": "blue" 
} 

Ключи/дорожки будет начинаться с косой черты, но Firebase все равно будет рассматривать их как по отношению к работе, на которой update называется.

+0

превосходное решение, отлично работает. если кому-то это нужно для аналогичной цели, обратите внимание, что: ** (a) ** массивы распространяются на 'objs' с ключом на' index', то есть 'jsonPointer.dict (['a', 'b', 'c' ]) == {1: 'a', 2: 'b', 3: 'c'} '(это то, что Firebase будет делать с ними в любом случае), и ** (b) ** добавит' null' s и 'undefined's к результирующему« объекту »- я просто добавил простой цикл для удаления этих значений перед тем, как нажать на firebase, [snippet is here] (https://runkit.com/brandonmp/583c60989f00610014775b5f) – Brandon

0

[У меня есть рабочий метод, но я не собираюсь accept этот ответ в надежде, что кто-то ж/больше опыта здесь может опин/поправьте меня, если я ошибаюсь]

После изменения переменная где хранить path от array к string, а также некоторые другие изменения в алгоритме в ОП, это решение, кажется, работает до сих пор:

export const flattenForUpdate = (obj, path = '') => { 
    var newData = {} 

    const _flatten = (_obj, _path) => { 
    try { 
     for (var propName in _obj) { 
      if (_obj.hasOwnProperty(propName)) { 
       if (typeof _obj[propName] === "object" 
        && typeof _obj[propName] !== null 
        && Array.isArray(_obj[propName]) === false) { 
        _flatten(_obj[propName], _path + "/" + String(propName)) 
       } else if (_obj[propName] == null) { 
        throw new Error("Null/Undefined in Firebase data update payload!") 
       } else { 
        let thisPath = _path + "/" + String(propName) 
        newData[thisPath] = _obj[propName] 
       } 
      } 
     } 
    } catch (err) { 
     console.error("ERROR IN FLATTENING", err) 
    } 
    } 
    _flatten(obj, path) 
    return newData 

} 

это немного грязный, но ключевые вынос являются :

  • вернет Arrays так же, как это будет primitive, б/с в firebase arrays странные и не могут реально поддерживать атомные гранулированных обновления, поэтому простейший случай, чтобы перезаписать текущий массив в firebase
  • он будет бросать на null а также undefined. undefined в update() полезной нагрузки вызовет ошибку, которая предотвращает все записи, и null будет удалить любое значение было там, что, по-видимому, не желательно
  • это укупорочные (я думаю). Я никогда не использовал closures перед afaik, но, насколько мне известно, потому что он рекурсивный, любое внутреннее состояние должно храниться в другом context, чем функция, выполняющая фактическую рекурсию, что, очевидно, является причиной того, почему Бог/ECMA/Douglas Crockford или тот, кто сделал JS включил JS.
Смежные вопросы