import ast


def merge_attrs(source_attrs, collection_of_attrs, attr_resolver,
                attrs_to_ignore=None):
    """Merge two attribute dictionaries into the target using the input resolver.

    Parameters
    ----------
    source_attrs : dict
        Source dictionary with attributes (the other attributes will be
        merged into it and a new object will be returned)
    collection_of_attrs : iterable of dict
        Collection of dictionaries to merge into the target dictionary
    attr_resolver : dict
        Dictionary containing attribute resolvers, its keys are attribute
        names and its values are functions applied to the set of attribute
        values in order to resolve this set to a single value
    attrs_to_ignore : iterable, optional
        Set of attributes to ignore (will not be included in the merged
        node or edges incident to this merged node)
    """
    result = source_attrs.copy()

    if attrs_to_ignore is None:
        attrs_to_ignore = []

    all_keys = set(
        sum([list(attrs.keys()) for attrs in collection_of_attrs], []))

    def _preprocess(k, attrs):
        if k == "paper" and isinstance(attrs, str):
            return ast.literal_eval(attrs)
        else:
            return attrs

    for k in all_keys:
        if k not in attrs_to_ignore:
            if k in attr_resolver:
                result[k] = attr_resolver[k](
                    ([_preprocess(k, result[k])] if k in result else []) + [
                        _preprocess(k, attrs[k])
                        for attrs in collection_of_attrs if k in attrs
                    ]
                )
            else:
                result[k] = None
    return result