import itertools


def group_into_dict(items, key_fn):
    """
    Groups items into a dictionary based on a key generated by a given function

    Examples:
        items = [
            Car(make="Honda", model="Civic"),
            Car(make="Honda", model="Accord"),
            Car(make="Ford", model="F150"),
            Car(make="Ford", model="Focus"),
        ]
        group_into_dict(items, lambda car: car.make) == {
            "Honda": [Car(make="Honda", model="Civic"), Car(make="Honda", model="Accord")],
            "Ford": [Car(make="Ford", model="F150"), Car(make="Ford", model="Focus")],
        }

    Args:
        items (Iterable[T]): An iterable of objects to group into a dictionary
        key_fn (Callable[[T], Any]): A function that will take an individual item and produce a dict key

    Returns:
        Dict[Any, T]: A dictionary with keys produced by the key function paired with a list of all the given
            items that produced that key.
    """
    sorted_items = sorted(items, key=key_fn)
    return {
        key: list(values_iter)
        for key, values_iter in itertools.groupby(sorted_items, key=key_fn)
    }