#python #dictionary
#python #словарь
Вопрос:
Я хотел бы иметь систему, которая назначает разные команды разным категориям!
Вот как мы получаем команды:
commands = []
for k, v in config['commands'].items():
commands.append(config['commands'][k]['command'])
Существует файл .yaml, в котором каждая команда имеет разные свойства. Одна команда и одна категория:
command_one:
category: "Information"
command: "info"
command_two:
category: "Owneronly"
command: "ban"
command_two:
category: "Owneronly"
command: "kick"
Теперь я хотел бы отсортировать команды в соответствии с их категорией в коллекции, которая выглядит следующим образом:
sortedCommands = {"Owneronly": ["ban", "kick"], "Information": ["info"]}
Редактировать: вы можете получить категорию из команды следующим образом:
for k, v in config['commands'].items():
commands.append(config['commands'][k]['category'])
Ответ №1:
Это несколько подробный (для наглядности) способ сделать это:
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
Примечание: append()
on lists работает на месте, поэтому вам нужно извлечь текущий список, добавить и переназначить его обратно.
Другим, несколько более кратким вариантом является групповое поведение:
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"))
Для распаковки требуется немного времени, но вот что он делает:
- Сортируйте итерацию с помощью
sorted()
и функции, которая определяет порядок сортировки. - Представленная здесь функция
lambda
получает значение категории. Это, естественно, сортируется в алфавитно-цифровом порядке. - Затем используйте
groupby
для группировки по той же функции «ключ». - После сортировки и группировки для каждого элемента в результирующей итерации назначьте категорию в качестве ключа, а список результатов — в качестве значения в новом словаре.
Здесь есть небольшая загвоздка, поскольку он возвращает полный dict для каждого исходного элемента, поэтому нам нужно очистить некоторую информацию:
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()}
Все это делается для каждого элемента категории, возьмите список словарей и сохраните только имя команды, используя понимание списка.
Полный пример в действии:
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
Этот подход, хотя и более плотный, более функциональный и использует основные библиотеки Python. В результате, я бы поспорил, что это быстрее.
Ответ №2:
Что-то вроде этого должно работать:
from collections import defaultdict
sortedCommands = defaultdict(list)
for cfg in config['commands'].values():
sortedCommands[cfg['category']].append(cfg['command'])
Я лично не стал бы использовать здесь слово «сортировать», потому что сортировка не выполняется. Эта операция просто изменяет форму данных — я бы предпочел имя, подобное commands_by_category
. Но, конечно, вы можете использовать любые имена, которые хотите.