Reputation: 17
I would like to have a system that assigns different commands to different categories!
This is, how we get the commands:
commands = []
for k, v in config['commands'].items():
commands.append(config['commands'][k]['command'])
There is a .yaml file in which each command has different properties. One command and one category:
command_one:
category: "Information"
command: "info"
command_two:
category: "Owneronly"
command: "ban"
command_two:
category: "Owneronly"
command: "kick"
Now I would like to sort the commands according to their category in a collection that looks like this:
sortedCommands = {"Owneronly": ["ban", "kick"], "Information": ["info"]}
Edit: You can get the category from the command like this:
for k, v in config['commands'].items():
commands.append(config['commands'][k]['category'])
Upvotes: 0
Views: 55
Reputation: 21230
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:
sorted()
and a function that decides sort order.lambda
that gets the category value. This is naturally sorted alphanumerically.groupby
to group by that same 'key' function.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.
Upvotes: 1
Reputation: 13589
Something like this should work:
from collections import defaultdict
sortedCommands = defaultdict(list)
for cfg in config['commands'].values():
sortedCommands[cfg['category']].append(cfg['command'])
I personally wouldn't use the word "sort" here, because there is no sorting going on. This operation simply reshapes the data - I would prefer a name like commands_by_category
. But of course you are free to use whatever names you want.
Upvotes: 0