#python #python-3.x #sorting #tuples
#python #python-3.x #сортировка #кортежи
Вопрос:
Допустим, у меня есть такой словарь:
a = {1:"a", 3:"c", 5:"e", 4:"d" ,2:"b", 6:"f", 7:"g"}
И я преобразую его в список кортежей:
b = list(a.items())
Теперь я хочу отсортировать список на основе первого значения кортежа. У меня есть три способа сделать это:
from operator import itemgetter
b.sort(key=itemgetter(0))
b.sort(key=lambda x: x[::-1])
b = sorted(b, key=lambda t: (t[0]))
В любом случае, если я печатаю b
, я получаю отсортированный список:
print(b)
>>> [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f'), (7, 'g')]
Однако, если я действительно использую b
в процессе, оказывается, что он не отсортирован. Я протестировал несколько вещей, но текущая, которая у меня есть, это:
batch_count = len(b) // 3
batches = [[] for _ in range(batch_count)]
for index, vec in enumerate(b):
batches[index % batch_count].append(vec)
Это группируется b
в списки не менее 3. batches
Однако, если я печатаю, я получаю исходный порядок из a
, за исключением 7
ожидаемого.
print(batches)
>>> [[(1, 'a'), (3, 'c'), (5, 'e'), (7, 'g')], [(2, 'b'), (4, 'd'), (6, 'f')]]
Кто-нибудь знает, почему и как я могу это предотвратить?
Комментарии:
1. Результат — это именно то, что вы должны получить; тот факт, что ваш оригинал
dict
имел почти тот же порядок (игнорируя границы пакета), является совпадением.2. Какой результат вы ожидали? Группировка элементов
b
в пакеты, естественно, изменит их порядок. Обратите внимание, что каждая группа по-прежнему упорядочена должным образом.
Ответ №1:
Это не тот же порядок. В результате он разделяется на четные / нечетные позиции index % batch_count
.
При batch_count
значении 2 у вас есть index % 2
, которое будет чередоваться между 0
и 1
:
>>> [i % 2 for i in range(10)]
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
Это приводит к тому, что в первой вложенной коллекции остаются четные позиции, а во второй — коэффициенты.
Комментарии:
1. Я только что протестировал с другим dict, и вы правы! Я потратил на это около часа и не понял! Я тупой! Спасибо!
2. Нет проблем. Если вы сомневаетесь, проверьте свои промежуточные данные. А для группировки ознакомьтесь с разделом рецептов в
itertools
документации. Там может быть существующее решение, которое вам поможет. Это отличный модуль.
Ответ №2:
Вывод нормальный. Вы можете попробовать это без сортировки вообще, чтобы увидеть разницу.
batch_count = len(b) // 3
batches = [[] for _ in range(batch_count)]
for index, vec in enumerate(b):
batches[index % batch_count].append(vec)
создает два подсписка из вашего исходного списка в вашем случае, evens и odds. Каждый из них отсортирован.
Ответ №3:
Вы могли бы использовать grouper
функцию для разделения списка на список списков размера n
.
from itertools import zip_longest
def grouper(n, iterable):
args = [iter(iterable)] * n
return ([e for e in t if e != None] for t in zip_longest(*args))
b = [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e'), (6, 'f'), (7, 'g')]
batches = list(grouper(3, b))
print(batches)
Вывод:
[[(1, 'a'), (2, 'b'), (3, 'c')], [(4, 'd'), (5, 'e'), (6, 'f')], [(7, 'g')]]
Комментарии:
1. Хотя мне нравится ваше мышление, но тот факт, что он создает итерацию только с первой записью, имеющей значение, а затем перебирает ее, создает ненужный перебор. Поэтому цикл for работает быстрее. Хотя и очень незначительно. Тем не менее, очень хорошее решение. Спасибо!
2. @mnsr Если вы достигли того же самого после пересмотра вашего for-цикла, то да, сохраните это. Это решение является более общим и работает для каждого типа списка, который вы хотите сгруппировать по номеру. Накладные расходы незначительны. Спасибо за поддержку, приветствия 🙂