/**
 * Maps each entry of a Map based on a provided key-generating function.
 *
 * This function takes a Map and a function that generates a key from each value-key pair.
 * It returns a new Map where the keys are generated by the key function and the values are
 * the corresponding values from the original map. If multiple entries produce the same key,
 * the last value encountered will be used.
 *
 * @template K - The type of keys in the original Map.
 * @template V - The type of values in the original Map.
 * @template K2 - The type of keys to produce in the returned Map.
 * @param map - The map of entries to be mapped.
 * @param getKeyFromEntry - A function that generates a key from a value-key pair.
 * @returns A Map where the generated keys are mapped to each entry's value.
 *
 * @example
 * const map = new Map([
 *   ['x', { type: 'fruit', name: 'apple' }],
 *   ['y', { type: 'fruit', name: 'banana' }],
 *   ['z', { type: 'vegetable', name: 'carrot' }]
 * ]);
 * const result = keyBy(map, item => item.type);
 * // result will be:
 * // Map(2) {
 * //   'fruit' => { type: 'fruit', name: 'banana' },
 * //   'vegetable' => { type: 'vegetable', name: 'carrot' }
 * // }
 */
export function keyBy<K, V, K2>(
  map: Map<K, V>,
  getKeyFromEntry: (value: V, key: K, object: Map<K, V>) => K2
): Map<K2, V> {
  const result = new Map<K2, V>();

  for (const [key, value] of map) {
    const newKey = getKeyFromEntry(value, key, map);
    result.set(newKey, value);
  }

  return result;
}
