Example #1
0
def joindicts(dest, *dictlist):
    """Merges one or more dictionaries into dest recursively.
    Dictionaries are merged, lists are merged. Scalars and strings are ignored.
    Returns the generated result.

    To merge with overwrite, use the native dict update method.
    """
    result = dest
    for inputdict in dictlist:
        for key, value in inputdict.items():
            # If this is a new value, take as is
            if key not in result:
                result[key] = value
                continue

            # Handle collision
            existing_value = dest[key]
            if typecheck.is_dict(existing_value) and typecheck.is_dict(value):
                result[key] = joindicts(existing_value, value)
            elif typecheck.is_list(existing_value) and typecheck.is_list(
                    value):
                result[key] = existing_value + value
            else:
                raise Exception("Failed to merge dictionaries: " +
                                f"could not resolve collision on key '{key}'")

    return result
Example #2
0
def flatten_dict(d, prefix='', result=None):
    "Flattens a dictionary using path separators"
    # recursive algorithm where each pass gives the result object to the next patch
    # The key algorithm boils down to:
    # - If its not a scalar (dict or list), build the prefix
    # - If its a scalar (string or number), assign the value

    result = {} if result is None else result

    if typecheck.is_dict(d):
        # dictionaries add a / behind them for non-root ones
        if prefix:
            prefix = prefix + '/'

        for key, value in d.items():
            if typecheck.is_scalar(value):
                result[f"{prefix}{key}"] = value
            else:
                flatten_dict(value, prefix=prefix + key, result=result)
    elif typecheck.is_list(d):
        for idx, value in enumerate(d):
            if typecheck.is_scalar(value):
                result[f'{prefix}[{idx}]'] = value
            else:
                flatten_dict(value, prefix=f'{prefix}[{idx}]', result=result)
    else:
        raise Exception('Unsupported type ' + str(type(d)))

    return result
Example #3
0
def struct_to_json(obj):
    "Recursively serializes a binary to a dictionary"
    from mhdata import typecheck

    if hasattr(obj, 'as_dict'):
        obj = obj.as_dict()

    if typecheck.is_dict(obj):
        return {key: struct_to_json(value) for key, value in obj.items()}
    elif typecheck.is_flat_iterable(obj):
        return [struct_to_json(value) for value in obj]

    return obj
Example #4
0
def flatten(obj, *, nest, prefix={}):
    """Flattens a nested object into a list of flat dictionaries
    nest is a list of fieldnames.
    Do not use prefix, its internal.
    """
    # This is a recursive algorithm.
    # We iteratively step through "nest levels".

    # BASE CASE
    if not nest:
        items = obj
        if not typecheck.is_flat_iterable(obj):
            items = [obj]

        # Error checking, items must be dictionaries
        if any(not typecheck.is_dict(item) for item in items):
            raise TypeError("Flattened entries must be a mapping type")

        # Extend all items with the prefix and return them
        return [{**prefix, **item} for item in items]

    # Validation
    if not isinstance(obj, collections.Mapping):
        raise ValueError("Object is not sufficiently deep for flattening")

    # Return multiple results by stepping down one depth level,
    # and recursively calling it for the sub-items
    current_nest = nest[0]
    remaining_nest = nest[1:]

    results = []
    for nest_value, remaining_data in obj.items():
        sub_prefix = {**prefix, current_nest: nest_value}

        # RECURSIVE CALL
        sub_flattened = flatten(remaining_data,
                                nest=remaining_nest,
                                prefix=sub_prefix)
        results.extend(sub_flattened)

    return results