docs: function buildFromIndexMethods() {
  
    var methods = {
      'forEach': {
        base: forEachAsNative
      },
      'map': {
        wrapper: enhancedMapping
      },
      'some every': {
        wrapper: enhancedMatching
      },
      'findIndex': {
        wrapper: enhancedMatching,
        result: indexResult
      },
      'reduce': {
        apply: applyReduce
      },
      'filter find': {
        wrapper: enhancedMatching
      },
      'reduceRight': {
        apply: applyReduce,
        slice: sliceArrayFromRight,
        clamp: clampStartIndexFromRight
      }
    };
  
    forEachProperty(methods, function(opts, key) {
      forEach(spaceSplit(key), function(baseName) {
        var methodName = baseName + 'FromIndex';
        var fn = createFromIndexWithOptions(baseName, opts);
        defineInstanceWithArguments(sugarArray, methodName, fn);
      });
    });
  
    function forEachAsNative(fn) {
      forEach(this, fn);
    }
  
    // Methods like filter and find have a direct association between the value
    // returned by the callback and the element of the current iteration. This
    // means that when looping, array elements must match the actual index for
    // which they are being called, so the array must be sliced. This is not the
    // case for methods like forEach and map, which either do not use return
    // values or use them in a way that simply getting the element at a shifted
    // index will not affect the final return value. However, these methods will
    // still fail on sparse arrays, so always slicing them here. For example, if
    // "forEachFromIndex" were to be called on [1,,2] from index 1, although the
    // actual index 1 would itself would be skipped, when the array loops back to
    // index 0, shifting it by adding 1 would result in the element for that
    // iteration being undefined. For shifting to work, all gaps in the array
    // between the actual index and the shifted index would have to be accounted
    // for. This is infeasible and is easily solved by simply slicing the actual
    // array instead so that gaps align. Note also that in the case of forEach,
    // we are using the internal function which handles sparse arrays in a way
    // that does not increment the index, and so is highly optimized compared to
    // the others here, which are simply going through the native implementation.
    function sliceArrayFromLeft(arr, startIndex, loop) {
      var result = arr;
      if (startIndex) {
        result = arr.slice(startIndex);
        if (loop) {
          result = result.concat(arr.slice(0, startIndex));
        }
      }
      return result;
    }
  
    // When iterating from the right, indexes are effectively shifted by 1.
    // For example, iterating from the right from index 2 in an array of 3
    // should also include the last element in the array. This matches the
    // "lastIndexOf" method which also iterates from the right.
    function sliceArrayFromRight(arr, startIndex, loop) {
      if (!loop) {
        startIndex += 1;
        arr = arr.slice(0, max(0, startIndex));
      }
      return arr;
    }
  
    function clampStartIndex(startIndex, len) {
      return min(len, max(0, startIndex));
    }
  
    // As indexes are shifted by 1 when starting from the right, clamping has to
    // go down to -1 to accommodate the full range of the sliced array.
    function clampStartIndexFromRight(startIndex, len) {
      return min(len, max(-1, startIndex));
    }
  
    function applyReduce(arr, startIndex, fn, context, len, loop) {
      return function(acc, val, i) {
        i = getNormalizedIndex(i + startIndex, len, loop);
        return fn.call(arr, acc, val, i, arr);
      };
    }
  
    function applyEach(arr, startIndex, fn, context, len, loop) {
      return function(el, i) {
        i = getNormalizedIndex(i + startIndex, len, loop);
        return fn.call(context, arr[i], i, arr);
      };
    }
  
    function indexResult(result, startIndex, len) {
      if (result !== -1) {
        result = (result + startIndex) % len;
      }
      return result;
    }
  
    function createFromIndexWithOptions(methodName, opts) {
  
      var baseFn = opts.base || Array.prototype[methodName],
          applyCallback = opts.apply || applyEach,
          sliceArray = opts.slice || sliceArrayFromLeft,
          clampIndex = opts.clamp || clampStartIndex,
          getResult = opts.result,
          wrapper = opts.wrapper;
  
      return function(arr, startIndex, args) {
        var callArgs = [], argIndex = 0, lastArg, result, len, loop, fn;
        len = arr.length;
        if (isBoolean(args[0])) {
          loop = args[argIndex++];
        }
        fn = args[argIndex++];
        lastArg = args[argIndex];
        if (startIndex < 0) {
          startIndex += len;
        }
        startIndex = clampIndex(startIndex, len);
        assertArgument(args.length);
        fn = wrapper ? wrapper(fn, lastArg) : fn;
        callArgs.push(applyCallback(arr, startIndex, fn, lastArg, len, loop));
        if (lastArg) {
          callArgs.push(lastArg);
        }
        result = baseFn.apply(sliceArray(arr, startIndex, loop), callArgs);
        if (getResult) {
          result = getResult(result, startIndex, len);
        }
        return result;
      };
    }
  }