function populate(state, path, populates, notSetValue) {
  const splitPath = path.split('/').filter(Boolean) // Drop falsey values (compact)
  // append 'data' prefix to path if it is not a top level path
  const pathArr =
    topLevelPaths.indexOf(splitPath[0]) === -1
      ? ['data', ...splitPath]
      : splitPath
  const dotPath = pathArr.join('.')
  // Gather data from top level if path is profile (handles populating profile)
  const data = get(state, dotPath, notSetValue)

  // Return notSetValue for undefined child
  if (!state || data === notSetValue) {
    return notSetValue
  }
  // Return null for null child
  if (data === null) {
    return null
  }

  // check for if data is single object or a list of objects
  const populatesForData = getPopulateObjs(
    typeof populates === 'function'
      ? populates(pathArr.slice(-1)[0], data) // pass last slice in path
      : populates
  )

  if (Array.isArray(data)) {
    // When using a path in ordered, data will be an array instead of an object
    // and data is located at the `value` prop
    const someArrayItemHasKey = (array) => (key) =>
      some(array, (item) => has(item, key))

    // Check items within the list to see if value exists for some populate parameters
    const dataHasPopulateChilds = some(populatesForData, (populate) =>
      someArrayItemHasKey(data)(['value', populate.child])
    )

    // Populate if populate children exist
    if (dataHasPopulateChilds) {
      return data.map(({ key, value: dataValue }) => {
        const populatedValue = populatesForData
          .map((p) => populateChild(state, dataValue, p))
          .reduce((acc, v) => defaultsDeep(v, acc), dataValue)

        return {
          key,
          value: populatedValue
        }
      })
    }

    // return unpopulated data if no populates have values
    return data
  }

  // check each populate child parameter for existence
  const dataHasPopulateChilds = some(populatesForData, (p) =>
    has(data, p.child)
  )

  // Single object that contains at least one child parameter
  if (dataHasPopulateChilds) {
    return populatesForData
      .map((p) => populateChild(state, data, p))
      .reduce((acc, v) => defaultsDeep(v, acc), data)
  }

  // Return for profile since it is a single object (following is for a list of objects)
  // TODO: Improve this logic to allow for other paths containing profile
  if (pathArr.indexOf('profile') !== -1) {
    return data
  }

  // Data is a map of objects, each value has parameters to be populated
  return mapValues(data, (child, childKey) => {
    // use child's key if doing ordered populate
    const key = pathArr[0] === 'ordered' ? child.key : childKey
    // get populate settings on item level (passes child if populates is a function)
    const populatesForDataItem = getPopulateObjs(
      typeof populates === 'function' ? populates(key, child) : populates
    )
    // confirm at least one populate value exists on child
    const dataHasPopulateChilds = some(populatesForDataItem, (p) =>
      has(child, p.child)
    )
    // return unmodified child if no populate params exist on child
    if (!dataHasPopulateChilds) {
      return child
    }
    // combine data from all populates to one object starting with original data
    return reduce(
      map(populatesForDataItem, (p) => populateChild(state, child, p)),
      (obj, v) => defaultsDeep(v, obj),
      child
    )
  })
}
Populate with data from multiple locations of redux state.
@param {object} state - Firebase state object (state.firebase in redux store)
@param {string} path - Path of parameter to load
@param {Array} populates - Array of populate config objects
@param {object|string|boolean} notSetValue - Value to return if value is not found
@returns {object} Data located at path within Immutable Object
@example <caption>Basic</caption>
import { compose } from 'redux'
import { connect } from 'react-redux'
import { firebaseConnect } from 'react-redux-firebase'
const populates = [{ child: 'owner', root: 'users' }]

const enhance = compose(
  firebaseConnect([
    { path: 'todos', populates } // load "todos" and matching "users" to redux
  ]),
  connect((state) => ({
    // this.props.todos loaded from state.firebase.data.todos
    // each todo has child 'owner' populated from matching uid in 'users' root
    // for loading un-populated todos use state.firebase.data.todos
    todos: populate(state.firebase, 'todos', populates),
  }))
)

export default enhance(SomeComponent)