Return the first match in a dictionary of target patterns

    .. versionadded:: 2014.7.0

    CLI Example:

    .. code-block:: bash

        salt '*' match.filter_by '{foo*: Foo!, bar*: Bar!}' minion_id=bar03

    Pillar Example:

    .. code-block:: jinja

        # Filter the data for the current minion into a variable:
        {% set roles = salt['match.filter_by']({
            'web*': ['app', 'caching'],
            'db*': ['db'],
        }, default='web*') %}

        # Make the filtered data available to Pillar:
        roles: {{ roles | yaml() }}

def filter_by(lookup,
              tgt_type='compound',
              minion_id=None,
              merge=None,
              merge_lists=False,
              default='default'):
    '''
    Return the first match in a dictionary of target patterns

    .. versionadded:: 2014.7.0

    CLI Example:

    .. code-block:: bash

        salt '*' match.filter_by '{foo*: Foo!, bar*: Bar!}' minion_id=bar03

    Pillar Example:

    .. code-block:: jinja

        # Filter the data for the current minion into a variable:
        {% set roles = salt['match.filter_by']({
            'web*': ['app', 'caching'],
            'db*': ['db'],
        }, default='web*') %}

        # Make the filtered data available to Pillar:
        roles: {{ roles | yaml() }}
    '''
    expr_funcs = dict(inspect.getmembers(sys.modules[__name__],
        predicate=inspect.isfunction))

    for key in lookup:
        params = (key, minion_id) if minion_id else (key, )
        if expr_funcs[tgt_type](*params):
            if merge:
                if not isinstance(merge, collections.Mapping):
                    raise SaltException(
                        'filter_by merge argument must be a dictionary.')

                if lookup[key] is None:
                    return merge
                else:
                    salt.utils.dictupdate.update(lookup[key], copy.deepcopy(merge), merge_lists=merge_lists)

            return lookup[key]

    return lookup.get(default, None)