Home > front end >  Python sort list of dicts by multiple dynamic keys, with None values at last position
Python sort list of dicts by multiple dynamic keys, with None values at last position

Time:11-04

I want to sort a list of dicts (dict_list) (that can also contain nested dicts) by a list of received keys (groups). The values inside the dicts can generally be None.

After sorting, None values should generally be palced in the last position. Example when i have the following group list and dict list

groups = ["key1", "key3.key4"]
dict_list = [
    {
        "key1": "abc",
        "key2": "def",
        "key3": {
            "key4": "ghi"
        },
        "key5": {
            "key6": "uvw"
        }
    }, 
    {
        "key1": "abc",
        "key2": "asd",
        "key3": {
            "key4": "abc"
        },
        "key5": {
            "key6": "uvw"
        }
    },    
    {
        "key1": None,
        "key2": "asd",
        "key3": {
            "key4": "abc"
        },
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": "abc",
        "key2": None,
        "key3": None,
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": "xyz",
        "key2": None,
        "key3": {
            "key4": "jklm"
        },
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": "abc",
        "key2": "dfd",
        "key3": {
            "key4": "ghi"
        },
        "key5": {
            "key6": "ers"
        }
    }
]

I would expect the following output:

dict_list = [
    {
        "key1": "abc",
        "key2": "asd",
        "key3": {
            "key4": "abc"
        },
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": "abc",
        "key2": "def",
        "key3": {
            "key4": "ghi"
        },
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": "abc",
        "key2": "dfd",
        "key3": {
            "key4": "ghi"
        },
        "key5": {
            "key6": "ers"
        }
    },
    {
        "key1": "abc",
        "key2": None,
        "key3": None,
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": "xyz",
        "key2": None,
        "key3": {
            "key4": "jklm"
        },
        "key5": {
            "key6": "uvw"
        }
    },
    {
        "key1": None,
        "key2": "asd",
        "key3": {
            "key4": "abc"
        },
        "key5": {
            "key6": "uvw"
        }
    }
]

I tried implementing this using pythons list sort functions and itertools.groupby, but i can't seem to wrap my head around it.

Thanks in advance!

CodePudding user response:

Try:

def get_values_from_dict(d, keys):
    out = []
    for k in keys:

        root = d
        for kk in k.split("."):
            root = root.get(kk)
            if not root:
                break

        out.append(root)

    return out


def key_function(lst):
    return [(v is None, v) for v in lst]


print(
    sorted(
        dict_list, key=lambda d: key_function(get_values_from_dict(d, groups))
    )
)

Prints:

[
    {
        "key1": "abc",
        "key2": "asd",
        "key3": {"key4": "abc"},
        "key5": {"key6": "uvw"},
    },
    {
        "key1": "abc",
        "key2": "def",
        "key3": {"key4": "ghi"},
        "key5": {"key6": "uvw"},
    },
    {
        "key1": "abc",
        "key2": "dfd",
        "key3": {"key4": "ghi"},
        "key5": {"key6": "ers"},
    },
    {"key1": "abc", "key2": None, "key3": None, "key5": {"key6": "uvw"}},
    {
        "key1": "xyz",
        "key2": None,
        "key3": {"key4": "jklm"},
        "key5": {"key6": "uvw"},
    },
    {
        "key1": None,
        "key2": "asd",
        "key3": {"key4": "abc"},
        "key5": {"key6": "uvw"},
    },
]
  • Related