Inner join using left and right key functions

    :param left: left iterable to be joined
    :param right: right iterable to be joined
    :param function left_key_fn: function that produces hashable value from left objects
    :param function right_key_fn: function that produces hashable value from right objects
    :param join_fn: function called on joined left and right iterable items to complete join
    :rtype: list

def _inner_join(left, right, left_key_fn, right_key_fn, join_fn=union_join):
    """ Inner join using left and right key functions

    :param left: left iterable to be joined
    :param right: right iterable to be joined
    :param function left_key_fn: function that produces hashable value from left objects
    :param function right_key_fn: function that produces hashable value from right objects
    :param join_fn: function called on joined left and right iterable items to complete join
    :rtype: list
    """
    joiner = defaultdict(list)
    for ele in right:
        joiner[right_key_fn(ele)].append(ele)
    joined = []
    for ele in left:
        for other in joiner[left_key_fn(ele)]:
            joined.append(join_fn(ele, other))
    return joined