Skip to content

Instantly share code, notes, and snippets.

@richard-scott
Last active March 20, 2025 16:10
Show Gist options
  • Select an option

  • Save richard-scott/b2079f5578191eaa664b55fd64651134 to your computer and use it in GitHub Desktop.

Select an option

Save richard-scott/b2079f5578191eaa664b55fd64651134 to your computer and use it in GitHub Desktop.

Revisions

  1. richard-scott revised this gist Mar 20, 2025. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions dict_filters.py
    Original file line number Diff line number Diff line change
    @@ -3,12 +3,13 @@
    def remove_keys_recursive(original, remove):
    if isinstance(original, dict): # Handling dictionaries
    filtered_dict = {}
    for key, value in original.items():
    for key in sorted(original.keys()): # Sort keys alphabetically
    value = original[key]
    if key in remove:
    if isinstance(value, dict) and isinstance(remove[key], dict):
    # Recursively process nested dictionaries
    filtered_value = remove_keys_recursive(value, remove[key])
    if filtered_value: # Keep non-empty dictionaries
    if filtered_value: # Keep only non-empty dictionaries
    filtered_dict[key] = filtered_value
    elif isinstance(value, list) and isinstance(remove[key], list):
    # Remove specific values or dictionaries from the list
    @@ -18,7 +19,7 @@ def remove_keys_recursive(original, remove):
    else:
    filtered_dict[key] = remove_keys_recursive(value, remove.get(key, {}))

    return filtered_dict if filtered_dict else None # Remove empty dicts
    return dict(sorted(filtered_dict.items())) if filtered_dict else None # Sort final dict keys

    elif isinstance(original, list): # Handling lists
    return remove_items_from_list(original, remove)
    @@ -68,7 +69,7 @@ def filters(self):
    'dict_recursive_remove': custom_filter
    }

    # The following was used to test the above code before using it as a filter in Ansible.

    if __name__ == '__main__':
    # Example Dictionary with Lists
    original_dict = {
  2. richard-scott created this gist Mar 20, 2025.
    97 changes: 97 additions & 0 deletions dict_filters.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,97 @@
    from ansible.errors import AnsibleFilterError

    def remove_keys_recursive(original, remove):
    if isinstance(original, dict): # Handling dictionaries
    filtered_dict = {}
    for key, value in original.items():
    if key in remove:
    if isinstance(value, dict) and isinstance(remove[key], dict):
    # Recursively process nested dictionaries
    filtered_value = remove_keys_recursive(value, remove[key])
    if filtered_value: # Keep non-empty dictionaries
    filtered_dict[key] = filtered_value
    elif isinstance(value, list) and isinstance(remove[key], list):
    # Remove specific values or dictionaries from the list
    filtered_dict[key] = remove_items_from_list(value, remove[key])
    elif remove[key] is None:
    continue # Fully remove this key
    else:
    filtered_dict[key] = remove_keys_recursive(value, remove.get(key, {}))

    return filtered_dict if filtered_dict else None # Remove empty dicts

    elif isinstance(original, list): # Handling lists
    return remove_items_from_list(original, remove)

    return original # Return non-dict, non-list values as is


    def remove_items_from_list(lst, remove_list):
    """
    Removes elements from a list, including dictionaries, based on remove_list.
    """
    if not isinstance(remove_list, list):
    return lst # If remove_list isn't a list, do nothing

    result = []
    for item in lst:
    if isinstance(item, dict):
    # Keep dictionary items that don't fully match any dictionary in remove_list
    if not any(is_dict_match(item, remove_item) for remove_item in remove_list):
    result.append(item)
    else:
    # Remove non-dict items directly
    if item not in remove_list:
    result.append(item)

    return result


    def is_dict_match(dict1, dict2):
    """
    Checks if dict1 matches dict2 (i.e., if dict1 contains all key-value pairs in dict2).
    """
    if not isinstance(dict1, dict) or not isinstance(dict2, dict):
    return False # Only compare dicts
    return all(k in dict1 and dict1[k] == v for k, v in dict2.items())


    def custom_filter(data, remove):
    try:
    return remove_keys_recursive(data, remove)
    except Exception as e:
    raise AnsibleFilterError(f"Error in filter: {e}")

    class FilterModule(object):
    def filters(self):
    return {
    'dict_recursive_remove': custom_filter
    }

    # The following was used to test the above code before using it as a filter in Ansible.
    if __name__ == '__main__':
    # Example Dictionary with Lists
    original_dict = {
    "a": [1, 2, 3, 4],
    "b": {
    "c": [5, 6, 7],
    "d": [{"e": 10, "f": 20}, {"e": 30, "f": 40}]
    },
    "g": [100, 200, 300]
    }

    remove_dict = {
    "a": [2, 3], # Remove 2 and 3 from list "a"
    "b": {
    "c": [6], # Remove 6 from list "c"
    "d": [{"e": 10}] # Remove dicts inside list where "e" == 10
    },
    "g": None # Remove the entire list "g"
    }

    filtered_dict = remove_keys_recursive(original_dict, remove_dict)

    # Remove top-level empty dictionaries (if necessary)
    filtered_dict = {k: v for k, v in filtered_dict.items() if v is not None}

    print(filtered_dict)