Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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'])

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
245 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...