Изменение порядка dict с индивидуальным порядком

Учитывая диктат d, теперь, когда мы знаем, начиная с Python 3.7, что порядок (вставки) сохраняется, существует ли встроенный способ запросить тот же диктовку с теми же ключами, за исключением того, что некоторые ключи k1, k2,... должны приходи первым?

Пример:

  • ключ a (если есть) должен быть первым,
  • ключ first (если есть) должен присутствовать рядом

Я придумал это:

def reorder_dict(d, first_keys):
    new_keys = [k for k in first_keys if k in d.keys()] + [k for k in d.keys() if k not in first_keys]
    new_d = {k: d[k] for k in new_keys}
    return new_d

d1 = {"c": 3, "b": 2, "a": 1}
reorder_dict(d1, ["a", "first"])    # {'a': 1, 'c': 3, 'b': 2} as expected ; NB: "first" is not present

Есть ли встроенный способ сделать это более напрямую?

Вы хотите изменить существующий словарь или создать новый? Насколько велик len(first_keys) по сравнению с len(d)? Какой процент first_keys ожидается отсутствующим в d? (Я не отрицающий голос)

wim 17.04.2024 18:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я думаю, что ваш первоначальный подход хорош, но его можно улучшить с помощью небольшой корректировки. Часть if k not in first_keys делает ваш код квадратичным.

Этой неэффективности можно избежать:

def reorder_dict(d, first_keys):
    result = {k: d[k] for k in first_keys if k in d}
    result.update(d)
    return result

В Python 3.9+ однострочный вариант возможен с использованием оператора объединения dict

>>> d = {"c": 3, "b": 2, "a": 1}
>>> first_keys = ["a", "first"]
>>> {k: d[k] for k in first_keys if k in d} | d
{'a': 1, 'c': 3, 'b': 2}
first не является ключом в исходном словаре и не должен быть в результате. Это в примере ОП, чтобы показать, что решение не должно добавлять новые ключи.
Barmar 17.04.2024 18:02

Ключи, перечисленные в параметре упорядочения, должны располагаться в том же порядке, что и этот параметр. Ваш код этого не гарантирует. Он помещает их все вперед, но в том порядке, в котором они были в исходном словаре, а не в порядке параметра.

Barmar 17.04.2024 18:05

@Бармар, я не понимаю, что ты пытаешься сказать. Я перечитал вопрос, и этот результат кажется правильным, он не добавляет никаких новых ключей в словарь.

wim 17.04.2024 18:08

Я думал, что dict.fromkeys(["first", "b", "a"]) — это часть решения, а не создание входных данных.

Barmar 17.04.2024 18:14

Ваша функция по-прежнему не гарантирует, что ключи в результате будут в том же порядке, что и first_keys.

Barmar 17.04.2024 18:15

Он разбивает словарь на разделы, но не упорядочивает его.

Barmar 17.04.2024 18:15

@Бармар, я все еще не понимаю, о чем ты говоришь. Можете ли вы привести контрпример?

wim 17.04.2024 18:15
reorder_dict({'a': 1, 'c': 3, 'b': 2}, ['c', 'a']) должен вернуться {'c': 3, 'a': 1, 'b': 2}
Barmar 17.04.2024 18:18

@Barmar И это так. Покажите мне контрпример, для которого этот код не работает.

wim 17.04.2024 18:18

@Basj first не запрограммирован жестко в функции. О чем ты говоришь?

wim 17.04.2024 18:19

Вы правы, это работает. Я не до конца понимал, что tuple(v != x for v in first_keys) делает. Объяснение алгоритма имело бы большое значение — если такой эксперт, как я, этого не видит, какая надежда есть у новичков?

Barmar 17.04.2024 18:23

@Barmar Хорошо, я полностью удалил пример с ключом кортежа.

wim 17.04.2024 18:39

@wim Я говорил о более раннем редактировании, когда "first" был в состоянии, мой комментарий больше не полезен, я удалил его! PS: не могли бы вы повторно включить версию tuple(...)? Мне всегда нравится узнавать новые способы сделать это :) Спасибо за ваш ответ!

Basj 17.04.2024 19:33

PS: зачем нужен lookup = dict.fromkeys(first_keys), если бы lookup = first_keys не сработало бы?

Basj 17.04.2024 19:38

@Basj Это потому, что поиск в dict — это O (1), а поиск в списке — O (n). Я не буду повторно включать эту технику с кортежным ключом, потому что она уступает и сбивает с толку даже самопровозглашенных «экспертов». Но вы всегда можете просмотреть его в истории изменений: stackoverflow.com/revisions/78342346/3

wim 17.04.2024 20:10

@wim Да, насчет поиска O(1), но при выполнении dict.fromkeys(first_keys) создание словаря из исходного списка в любом случае требует O(n). О, понятно, ты делаешь это, чтобы ускорить очередь result.update({... if k not in lookup})?

Basj 17.04.2024 22:39

@Basj Да, это была идея, но теперь, когда вы об этом упомянули, это вообще-то не требуется. Отредактировал свой ответ.

wim 18.04.2024 00:27

Да, вы можете добиться этого, используя класс OrderedDict из модуля коллекций. Вы можете создать новый OrderedDict, в котором явно укажете порядок ключей, гарантируя, что определенные ключи будут первыми. Вот пример:

from collections import OrderedDict

# Your original dictionary

d = {'b': 2, 'c': 3, 'a': 1, 'first': 0}

# Define the order of keys

desired_order = ['a', 'first']

# Create a new OrderedDict with the desired order

ordered_d = OrderedDict((key, d[key]) for key in desired_order if key in d)

# Add remaining keys in their original order
for key in d:
    if key not in ordered_d:
        ordered_d[key] = d[key]

print(ordered_d)

Для этого вам не нужен OrderedDict. Начиная с версии 3.8 словари Python должны сохранять порядок вставки.

Barmar 17.04.2024 17:59

Чем это существенно отличается от того, что написал ОП?

Barmar 17.04.2024 18:00

Этот @Barmar ставит под сомнение следующий (не по теме) вопрос: начиная с Python 3.8, существует ли еще (много) вариантов использования OrderedDict ?

Basj 17.04.2024 18:08

В вашем примере, @HammerChi, вам следует удалить ключ first из d: это было сделано специально: функция не должна вызывать ошибку, если first находится в desired_order, но не в d (такой случай может случиться).

Basj 17.04.2024 18:09

@Basj Я не говорил, что он ни для чего не нужен, просто для этого не нужен.

Barmar 17.04.2024 18:16

Да, конечно @Barmar, я это понял. Но это заставило меня задуматься над таким оффтопом: полезен ли OrderedDict начиная с 3.8, и если да, то зачем?

Basj 17.04.2024 18:16

См. stackoverflow.com/questions/50872498/…

Barmar 17.04.2024 18:24

Как уже упоминал Бармар, я не уверен, как это вообще улучшает существующий код OP или какой смысл использовать OrderedDict, похоже, он не использует ни одну из функций OrderedDict.

wim 17.04.2024 18:45

Другие вопросы по теме