Take the JSON, find all its subparts satisfying the given condition and
    them by the given function. Other key-word arguments are passed to the function.

    .. testsetup::

        from pprint import pprint
        from proso.django.enrichment import enrich_by_predicate
        request = None

    .. testcode::

        def enricher(request, json_list, nested):
            for json_object in json_list:
                json_object['enriched'] = True

        enriched = enrich_by_predicate(
            request,
            [{'object_type': 'example_1'}, {'object_type': 'example_2'}],
            enricher,
            lambda x: True
        )

        pprint(enriched)

    .. testoutput::

        [{'enriched': True, 'object_type': 'example_1'},
         {'enriched': True, 'object_type': 'example_2'}]


    Args:
        request (django.http.request.HttpRequest): request which is currently processed
        json (list|dict): in-memory representation of JSON
        fun: function which is be applied on found objects
        predicate: function which is applied on all objects to determine which
            objects should be processed further
        skip_nested: ignore nested objects

    Returns:
        list|dict: transformed JSON

def enrich_by_predicate(request, json, fun, predicate, skip_nested=False, **kwargs):
    """
    Take the JSON, find all its subparts satisfying the given condition and
    them by the given function. Other key-word arguments are passed to the function.

    .. testsetup::

        from pprint import pprint
        from proso.django.enrichment import enrich_by_predicate
        request = None

    .. testcode::

        def enricher(request, json_list, nested):
            for json_object in json_list:
                json_object['enriched'] = True

        enriched = enrich_by_predicate(
            request,
            [{'object_type': 'example_1'}, {'object_type': 'example_2'}],
            enricher,
            lambda x: True
        )

        pprint(enriched)

    .. testoutput::

        [{'enriched': True, 'object_type': 'example_1'},
         {'enriched': True, 'object_type': 'example_2'}]


    Args:
        request (django.http.request.HttpRequest): request which is currently processed
        json (list|dict): in-memory representation of JSON
        fun: function which is be applied on found objects
        predicate: function which is applied on all objects to determine which
            objects should be processed further
        skip_nested: ignore nested objects

    Returns:
        list|dict: transformed JSON
    """
    time_start = time()
    collected = []
    memory = {'nested': False}

    def _collect(json_inner, nested):
        if nested and skip_nested:
            return
        if isinstance(json_inner, list):
            list(map(lambda x: _collect(x, nested), json_inner))
        elif isinstance(json_inner, dict):
            if predicate(json_inner):
                collected.append(json_inner)
                if nested:
                    memory['nested'] = True
            list(map(lambda x: _collect(x, True), list(json_inner.values())))
    _collect(json, False)
    if len(collected) > 0:
        fun(request, collected, memory['nested'], **kwargs)
    LOGGER.debug("enrichment of JSON by predicate by '%s' function took %s seconds", fun.__name__, (time() - time_start))
    return json