Итерация по паре итераций, отсортированных по атрибуту

Один из способов (самый быстрый?) Для итерации по паре итераций a а также b в отсортированном порядке, чтобы объединить их в цепочку и отсортировать итерируемо:

for i in sorted(chain(a, b)):
    print i

Например, если элементами каждой итерируемой являются:

a: 4, 6, 1
b: 8, 3

тогда эта конструкция будет производить элементы в порядке

1, 3, 4, 6, 8

Однако, если итерации перебирают объекты, это сортирует объекты по их адресу в памяти. Предполагая, что каждая итерация повторяется для одного и того же типа объекта,

  1. Каков самый быстрый способ перебора определенного атрибута объектов, отсортированных по этому атрибуту?

  2. Что, если выбранный атрибут отличается в разных предметах? Если повторяется a а также b оба перебирают объекты типа foo, который имеет атрибуты foo.x а также foo.y того же типа, как можно перебирать элементы a отсортировано по x а также b отсортировано по y?

Для примера № 2, если

a: (x=4,y=3), (x=6,y=2), (x=1,y=7)
b: (x=2,y=8), (x=2,y=3)

тогда элементы должны быть произведены в порядке

1, 3, 4, 6, 8

как прежде. Обратите внимание, что только x атрибуты из a и y атрибуты из b введите в сортировку и результат.

2 ответа

Решение

Тим Пицкер уже ответил за случай, когда вы используете один и тот же атрибут для каждой итерации. Если вы используете разные атрибуты одного и того же типа, вы можете сделать это следующим образом (используя комплексные числа в качестве готового класса, который имеет два атрибута одного типа):

В Python 2:

>>> a = [1+4j, 7+0j, 3+6j, 9+2j, 5+8j]
>>> b = [2+5j, 8+1j, 4+7j, 0+3j, 6+9j]
>>> keyed_a = ((n.real, n) for n in a)
>>> keyed_b = ((n.imag, n) for n in b)
>>> from itertools import chain
>>> sorted_ab = zip(*sorted(chain(keyed_a, keyed_b), key=lambda t: t[0]))[1]
>>> sorted_ab
((1+4j), (8+1j), (3+6j), 3j, (5+8j), (2+5j), (7+0j), (4+7j), (9+2j), (6+9j))

Так как в Python 3 zip() возвращает итератор, нам нужно привести его к списку, прежде чем пытаться его подписать:

>>> # ... as before up to 'from itertools import chain'
>>> sorted_ab = list(zip(*sorted(chain(keyed_a, keyed_b), key=lambda t: t[0])))[1]
>>> sorted_ab
((1+4j), (8+1j), (3+6j), 3j, (5+8j), (2+5j), (7+0j), (4+7j), (9+2j), (6+9j))

Ответ на вопрос 1: Вы можете предоставить key приписывать sorted(), Например, если вы хотите отсортировать по объекту .name затем используйте

sorted(chain(a, b), key=lambda x: x.name)

Что касается вопроса 2: я думаю, вам понадобится еще один атрибут для каждого объекта (например, foo.z в соответствии с предложением Zero Piraeus), к которому sorted(), поскольку эта функция не может определить, откуда поступил объект, который она сортирует в данный момент. В конце концов, он получает новый итератор от chain() который не содержит никакой информации о том, является ли текущий элемент из a или же b,

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