218
218

Reputation: 1824

Remove nested lists where all elements are None

I have a series of lists (within a dictionary) and I would like to remove any which only have None as elements. However, the lists have various different formattings e.g.

x=[None,[None],[None]]
x=[None,None,None]
x=[None,[None,None],[None,None]]

where any None could be replaced with a value.

Any example would be the following dictionary:

dict={"x1": [None,None,None],"x2": [None,[None],[None]], "x3": [None,[1],[0.5]],
      "x4":[None,[None,None],[None,None]],"x5":[None,[180,-360],[90,-180]]}

In this case I would like to keep (key,value) pairs "x3" and "x5" as they contain values that are not all None, but would like to remove "x1", "x2", and "x4" thus returning:

dict={"x3": [None,[1],[0.5]], "x5":[None,[180,-360],[90,-180]]}

A simple list comprehension on the various lists (x) in the dictionary such as

any(e is not None for e in x)

reads [None] entries as not None and returns True.

How can I determine which lists actually contain a value rather than [None] elements. I have looked at options for removing the square brackets, e.g. using itertools such as suggested here [Remove brackets from list in Python ], but these all rely on all the elements in the list being formatted in the same way, whereas I have mixed formatting. I am unable to change the formatting as it is required in this format elsewhere in the software.

Upvotes: 0

Views: 138

Answers (3)

jmd_dk
jmd_dk

Reputation: 13090

This solution is similar in nature to that of Keyur Potdar, but it works for all kinds of containers, not only list:

my_dict = {
    "x1": [None, None, None],
    "x2": [None, [None], [None]],
    "x3": [None, [1], [0.5]],
    "x4": [None, [None, None], [None, None]],
    "x5": [None, [180, -360], [90, -180]],
}

def all_None(val):
    # Check for None
    if val is None:
        return True
    # val is not None
    try:
        # val may be a container
        return all(all_None(el) for el in val)
    except:
        # val is neither None nor a container
        return False

my_dict_filtered = {key: val for key, val in my_dict.items() if not all_None(val)}
print(my_dict_filtered)

Upvotes: 1

Keyur Potdar
Keyur Potdar

Reputation: 7238

You can write a custom recursive function to check if all the elements of the nested list are None or not.

def is_none(a):
    return all(x is None if not isinstance(x, list) else is_none(x) for x in a)

my_dict = {"x1": [None, None, None],
           "x2": [None, [None], [None]],
           "x3": [None, [1], [0.5]],
           "x4": [None, [None, None], [None, None]],
           "x5": [None, [180, -360], [90, -180]]}

new_dict = {k: v for k, v in my_dict.items() if not is_none(v)}
print(new_dict)
# {'x3': [None, [1], [0.5]], 'x5': [None, [180, -360], [90, -180]]}

Upvotes: 3

Ben.T
Ben.T

Reputation: 29635

According to the comment of Benjamin, this function should return True if all the nested list in list_input contains the same value val_to_check and False otherwise:

def check_val(list_input, val_to_check):
    for elem in list_input:
        if isinstance(elem, list):
            if check_val(elem, val_to_check) == False:
                return False
        else:
            if elem != val_to_check:
                return False
    return True

Upvotes: 3

Related Questions