Groups a list of items by group id.

    Args:
        items (list): a list of the values to be grouped.
            if `by` is None, then each item is assumed to be a
            (groupid, value) pair.
        by (list): a corresponding list to group items by.
            if specified, these are used as the keys to group values
            in `items`
        sorted_ (bool): if True preserves the ordering of items within groups
            (default = True) FIXME. the opposite is true

    Returns:
        dict: groupid_to_items: maps a groupid to a list of items

    SeeAlso:
        group_indices - first part of a a more fine grained grouping algorithm
        apply_gropuing - second part of a more fine grained grouping algorithm

    CommandLine:
        python -m utool.util_dict --test-group_items

    Example:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_dict import *  # NOQA
        >>> import utool as ut
        >>> items = ['ham',     'jam',   'spam',     'eggs',    'cheese', 'bannana']
        >>> by    = ['protein', 'fruit', 'protein',  'protein', 'dairy',  'fruit']
        >>> groupid_to_items = ut.group_items(items, iter(by))
        >>> result = ut.repr2(groupid_to_items)
        >>> print(result)
        {'dairy': ['cheese'], 'fruit': ['jam', 'bannana'], 'protein': ['ham', 'spam', 'eggs']}

def group_items(items, by=None, sorted_=True):
    """
    Groups a list of items by group id.

    Args:
        items (list): a list of the values to be grouped.
            if `by` is None, then each item is assumed to be a
            (groupid, value) pair.
        by (list): a corresponding list to group items by.
            if specified, these are used as the keys to group values
            in `items`
        sorted_ (bool): if True preserves the ordering of items within groups
            (default = True) FIXME. the opposite is true

    Returns:
        dict: groupid_to_items: maps a groupid to a list of items

    SeeAlso:
        group_indices - first part of a a more fine grained grouping algorithm
        apply_gropuing - second part of a more fine grained grouping algorithm

    CommandLine:
        python -m utool.util_dict --test-group_items

    Example:
        >>> # ENABLE_DOCTEST
        >>> from utool.util_dict import *  # NOQA
        >>> import utool as ut
        >>> items = ['ham',     'jam',   'spam',     'eggs',    'cheese', 'bannana']
        >>> by    = ['protein', 'fruit', 'protein',  'protein', 'dairy',  'fruit']
        >>> groupid_to_items = ut.group_items(items, iter(by))
        >>> result = ut.repr2(groupid_to_items)
        >>> print(result)
        {'dairy': ['cheese'], 'fruit': ['jam', 'bannana'], 'protein': ['ham', 'spam', 'eggs']}
    """
    if by is not None:
        pairs = list(zip(by, items))
        if sorted_:
            # Sort by groupid for cache efficiency (does this even do anything?)
            # I forgot why this is needed? Determenism?
            try:
                pairs = sorted(pairs, key=op.itemgetter(0))
            except TypeError:
                # Python 3 does not allow sorting mixed types
                pairs = sorted(pairs, key=lambda tup: str(tup[0]))
    else:
        pairs = items

    # Initialize a dict of lists
    groupid_to_items = defaultdict(list)
    # Insert each item into the correct group
    for groupid, item in pairs:
        groupid_to_items[groupid].append(item)
    return groupid_to_items