function convertTo_id(val, schema) {
  if (val != null && val.$__ != null) {
    return val._doc._id;
  }
  if (val != null && val._id != null && (schema == null || !schema.$isSchemaMap)) {
    return val._id;
  }

  if (Array.isArray(val)) {
    const rawVal = val.__array != null ? val.__array : val;
    for (let i = 0; i < rawVal.length; ++i) {
      if (rawVal[i] != null && rawVal[i].$__ != null) {
        rawVal[i] = rawVal[i]._doc._id;
      }
    }
    if (utils.isMongooseArray(val) && val.$schema()) {
      return val.$schema()._castForPopulate(val, val.$parent());
    }

    return [].concat(val);
  }

  // `populate('map')` may be an object if populating on a doc that hasn't
  // been hydrated yet
  if (getConstructorName(val) === 'Object' &&
      // The intent here is we should only flatten the object if we expect
      // to get a Map in the end. Avoid doing this for mixed types.
      (schema == null || schema[schemaMixedSymbol] == null)) {
    const ret = [];
    for (const key of Object.keys(val)) {
      ret.push(val[key]);
    }
    return ret;
  }
  // If doc has already been hydrated, e.g. `doc.populate('map')`
  // then `val` will already be a map
  if (val instanceof Map) {
    return Array.from(val.values());
  }

  return val;
}
Retrieve the _id of `val` if a Document or Array of Documents.

@param {Array|Document|Any} val
@param {Schema} schema
@return {Array|Document|Any}
@api private