function removeCircularDependencies({ object, processed, visited }) {
    let queue = []

    processed.add(object)

    Object.keys(object).forEach((key) => {
      if (Array.isArray(object[key])) {
        object[key].forEach((item, index) => {
          if (isObject(item) && item.id) {
            if (visited.has(item)) {
              // if the property has already been visited (i.e. the current data object is a descendant of the property object)
              // replace it with a new object that only contains the id
              object[key][index] = { id: object[key][index].id }
            } else if (!processed.has(item)) {
              // if the property has not been processed,
              // add it to the queue to remove any circular references it contains
              queue = queue.concat(object[key])
            }
          }
        })
      } else if (isObject(object[key]) && object[key].id) {
        if (visited.has(object[key])) {
          // if the property has already been visited (i.e. the current data object is a descendant of the property object)
          // replace it with a new object that only contains the id
          object[key] = { id: object[key].id }
        } else if (!processed.has(object[key])) {
          // if the property has not been processed,
          // add it to the queue to remove any circular references it contains
          queue = queue.concat(object[key])
        }
      }
    })

    // add items to visited
    queue.forEach((item) => {
      visited.add(item)
    })

    // process the items
    queue.forEach((item) => {
      removeCircularDependencies({ object: item, processed, visited })
    })

    // remove items from visited
    queue.forEach((item) => {
      visited.delete(item)
    })
  }
/**
   * Remove any circular references from a raw data object
   * @param  {Object} args
   * @param  {Object} args.object - the object to check for circular references
   * @param  {Object} args.processed - a WeakSet of data objects already checked for circular references
   * @param  {Object} args.visited - a WeakSet of data objects already visited in the object hierarchy
   */
Remove any circular references from a raw data object
@param  {Object} args
@param  {Object} args.object - the object to check for circular references
@param  {Object} args.processed - a WeakSet of data objects already checked for circular references
@param  {Object} args.visited - a WeakSet of data objects already visited in the object hierarchy
Remove any circular references from a raw data object @param { Object } args @param { Object } args . object - the object to check for circular references @param { Object } args . processed - a WeakSet of data objects already checked for circular references @param { Object } args . visited - a WeakSet of data objects already visited in the object hierarchy
function removeCircularDependencies({ object, processed, visited }) {
    let queue = []
    processed.add(object)
    Object.keys(object).forEach((key) => {
      if (Array.isArray(object[key])) {
        object[key].forEach((item, index) => {
          if (isObject(item) && item.id) {
            if (visited.has(item)) {
              object[key][index] = { id: object[key][index].id }
            } else if (!processed.has(item)) {
              queue = queue.concat(object[key])
            }
          }
        })
      } else if (isObject(object[key]) && object[key].id) {
        if (visited.has(object[key])) {
          object[key] = { id: object[key].id }
        } else if (!processed.has(object[key])) {
          queue = queue.concat(object[key])
        }
      }
    })
    queue.forEach((item) => {
      visited.add(item)
    })
    queue.forEach((item) => {
      removeCircularDependencies({ object: item, processed, visited })
    })
    queue.forEach((item) => {
      visited.delete(item)
    })
  }
function removeCircularDependencies ( { object , processed , visited } ) { let queue = [ ] processed . add ( object ) Object . keys ( object ) . forEach ( ( key ) => { if ( Array . isArray ( object [ key ] ) ) { object [ key ] . forEach ( ( item , index ) => { if ( isObject ( item ) && item . id ) { if ( visited . has ( item ) ) { object [ key ] [ index ] = { id : object [ key ] [ index ] . id } } else if ( ! processed . has ( item ) ) { queue = queue . concat ( object [ key ] ) } } } ) } else if ( isObject ( object [ key ] ) && object [ key ] . id ) { if ( visited . has ( object [ key ] ) ) { object [ key ] = { id : object [ key ] . id } } else if ( ! processed . has ( object [ key ] ) ) { queue = queue . concat ( object [ key ] ) } } } ) queue . forEach ( ( item ) => { visited . add ( item ) } ) queue . forEach ( ( item ) => { removeCircularDependencies ( { object : item , processed , visited } ) } ) queue . forEach ( ( item ) => { visited . delete ( item ) } ) }
Remove any circular references from a raw data object
 @param  {Object} args
 @param  {Object} args.object - the object to check for circular references
 @param  {Object} args.processed - a WeakSet of data objects already checked for circular references
 @param  {Object} args.visited - a WeakSet of data objects already visited in the object hierarchy
Remove any circular references from a raw data object @param { Object } args @param { Object } args . object - the object to check for circular references @param { Object } args . processed - a WeakSet of data objects already checked for circular references @param { Object } args . visited - a WeakSet of data objects already visited in the object hierarchy
// if the property has already been visited (i.e. the current data object is a descendant of the property object) // replace it with a new object that only contains the id // if the property has not been processed, // add it to the queue to remove any circular references it contains // if the property has already been visited (i.e. the current data object is a descendant of the property object) // replace it with a new object that only contains the id // if the property has not been processed, // add it to the queue to remove any circular references it contains // add items to visited // process the items // remove items from visited