Maybe something like this?
def expand_dict(host):
# Create all of the possible key-value pairs for each key in the original dictionary
kv_pairs = [[(k, v) for v in vals.split(",")] for k, vals in host.items()]
# Find the number of dictionaries this would expand to
max_len = max(len(p) for p in kv_pairs)
# A list of possible values must either be the length of the number of dictionaries we expect, or length 1 so we can repeat the value max_len times
assert all(len(pairs) in {1, max_len} for pairs in kv_pairs)
# Expand all of the length 1 value lists to length max_len
updated_pairs = [p if len(p) == max_len else p * max_len for p in kv_pairs]
# Return a generator of dictionaries for each of the sets of key-value pairs
return (dict(pairs) for pairs in zip(*updated_pairs))
input_dict = {'hosts': [{'hostname': 'abc', 'ip': '127.0.0.1', 'extra_check_cmd': 'check-me'}, {'hostname': 'def', 'ip': '127.0.0.2', 'extra_check_cmd': 'check-it,check-this'}, {'hostname': 'ijk,uvw,xyz', 'ip': '127.0.0.3,127.0.0.4,127.0.0.5', 'extra': 'check-me'}]}
output_dict = {'hosts': [d for host in input_dict['hosts'] for d in expand_dict(host)]}
Breaking it down further, lets try it on an example. In this case, I'm using host = d['hosts'][2].
{'hostname': 'ijk,uvw,xyz',
'ip': '127.0.0.3,127.0.0.4,127.0.0.5',
'extra': 'check-me'}
The line kv_pairs = [[(k, v) for v in vals.split(",")] for k, vals in host.items()] gives us list of possible key-value pairs for the inner list of items.
[
[('hostname', 'ijk'), ('hostname', 'uvw'), ('hostname', 'xyz')],
[('ip', '127.0.0.3'), ('ip', '127.0.0.4'), ('ip', '127.0.0.5')],
[('extra', 'check-me')],
]
As you can see, there are 3 key-value pairs each for the "hostname" and "ip" keys, and only 1 pair for the "extra" key in the original host dictionary. The goal is to produce 3 dictionaries with 'extra': 'check-me' in each of them. So, we want to find the number of dictionaries we expect to produce.
The line max_len = max(len(p) for p in kv_pairs) gives us 3. Then, just as a sanity check, we want to make sure that every set of key-value pairs in kv_pairs is either length 1 or length 3. If it were anything else, the problem would not be well defined, so we add the assert assert all(len(pairs) in {1, max_len} for pairs in kv_pairs).
We then expand all of the length 1 kv pair lists to length 3 by repeating them. This list comprehension basically takes all of the length 3 lists and repeats the length 1 lists 3 times each so they're all the same length.
updated_pairs = [p if len(p) == max_len else p * max_len for p in kv_pairs]
[[('hostname', 'ijk'), ('hostname', 'uvw'), ('hostname', 'xyz')],
[('ip', '127.0.0.3'), ('ip', '127.0.0.4'), ('ip', '127.0.0.5')],
[('extra', 'check-me'), ('extra', 'check-me'), ('extra', 'check-me')]]
Now that everything is nice and lined up, we can start creating dictionaries. We can use zip() for this, which gives us iterators of tuples with items from each of the input iterators we pass in. I'm using Python's unpacking syntax to expand each list inside updated_kv_pairs as a separate argument to zip(). In other words,
zip(*updated_kv_pairs)
is the same as
zip(updated_kv_pairs[0], updated_kv_pairs[1], updated_kv_pairs[2])
Each iteration of zip() gives us the list of key-value pairs that go inside of a single dictionary we output. This gives us
{'hostname': 'ijk', 'ip': '127.0.0.3', 'extra': 'check-me'}
{'hostname': 'uvw', 'ip': '127.0.0.4', 'extra': 'check-me'}
{'hostname': 'xyz', 'ip': '127.0.0.5', 'extra': 'check-me'}