This is a somewhat verbose (for clarity) method of doing this:
commands_by_category = {} # Initialize a dictionary
for command in config['commands'].items(): # for each block
category = command.get('category') # unpack categoy
cmd = command.get('cmd') # unpack command
cmds = commands_by_category.get(category, []) # Get the current list, or a default empty list
cmds.append(cmd) # Append to list.
commands_by_category[category] = cmds # Reassign back to the dict
A note: append()
on lists works in-place so you have to pull out the current list, append, and reassign it back.
Another, somewhat more concise option is group-by behavior:
from itertools import groupby
from typing import Callable, Iterable
def group_by(iterable: Iterable, key: Callable) -> dict:
return {r[0]: list(r[1]) for r in groupby(sorted(iterable, key=key), key)}
group_by(config['commands'].items(), lambda x: x.get("category"))
This takes a bit to unpack, but here is what it's doing:
- Sort the iterable using
sorted()
and a function that decides sort order.
- The function provided here is the
lambda
that gets the category value. This is naturally sorted alphanumerically.
- Then use
groupby
to group by that same 'key' function.
- Once sorted and grouped, for each item in the resulting iterable, assign the category as a key and the list of results as the value in a new dictionary.
There is a small hitch here, in that it returns the full dict for each original item, so we have to clean out some info:
sorted_commands = group_by(config['commands'].items(), lambda x: x.get("category"))
commands_by_category = {k: [i.get('command') for i in v] for k, v in sorted_commands.items()}
All this is doing is for each category item, take the list of dictionaries and only keep the command name, using list comprehensions.
Full example in action:
from itertools import groupby
from typing import Callable, Iterable
def group_by(iterable: Iterable, key: Callable) -> dict:
return {r[0]: list(r[1]) for r in groupby(sorted(iterable, key=key), key)}
ls = [{"category": "Information", "command": "info"},
{"category": "Owneronly", "command": "ban"},
{"category": "Owneronly", "command": "kick"},
]
expected = {
"Information": ["info"],
"Owneronly": ["ban", "kick"],
}
sorted_commands = group_by(ls, lambda x: x.get("category"))
commands_by_category = {k: [i.get('command') for i in v] for k, v in sorted_commands.items()}
commands_by_category == expected # True
This approach is, while more dense, more functional and leverages Python core libraries. As a result, I'd wager it's faster.