function bucket(array, sorter, opt_obj) {
  const buckets = {};

  for (let i = 0; i < array.length; i++) {
    const value = array[i];
    const key = sorter.call(/** @type {?} */ (opt_obj), value, i, array);
    if (key !== undefined) {
      // Push the value to the right bucket, creating it if necessary.
      const bucket = buckets[key] || (buckets[key] = []);
      bucket.push(value);
    }
  }

  return buckets;
}
Splits an array into disjoint buckets according to a splitting function.
@param {IArrayLike<T>} array The array.
@param {function(this:S, T, number, !IArrayLike<T>):?} sorter Function to
    call for every element.  This takes 3 arguments (the element, the index
    and the array) and must return a valid object key (a string, number,
    etc), or undefined, if that object should not be placed in a bucket.
@param {S=} opt_obj The object to be used as the value of 'this' within
    sorter.
@return {!Object<!Array<T>>} An object, with keys being all of the unique
    return values of sorter, and values being arrays containing the items for
    which the splitter returned that key.
@template T,S