/** * Recursively navigates into nested collections inside `collection` by * following a sequence of properties `pathArray`, executing any methods * along the way, and returning the first `undefined` encountered. * @param {any} collection - The collection to traverse. * @param {Array<string|number|function()>} pathArray - A sequence of * property names, indexes, or functions to traverse into the `collection`. * @returns {any|undefined} The final value found, * otherwise the first `undefined` encountered. * @example * const largeViewModelObject = { * knockoutObservable: { * GetSomeValue() { return [1, 2, 3] } * } * } * getByPath(largeViewModelObject, ["knockoutObservable", "GetSomeValue", 2]); * // returns 3 */ const getByPath = (collection, pathArray) => { const [prop, ...leftoverProps] = pathArray; const value = collection[prop]; return leftoverProps.length == 0 ? value : getByPath( value instanceof Function ? collection[prop]() : value, leftoverProps ); };