Python: Как я могу добавлять значения к ключам и ключам к dicts

#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 . Но, конечно, вы можете использовать любые имена, которые хотите.