Iterate over a pair of iterations sorted by attribute

One way (the fastest way?) To iterate over a pair of iterations a and b in sorted order is to link them and sort the iterable chain:

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

For example, if the elements of each iterable are:

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

then this design would create elements of order

 1, 3, 4, 6, 8 

However, if iterations are repeated over objects, this sorts the objects by their memory address. Assuming each iteration of iteration over the same type of object,

  • What is the fastest way to iterate over a specific attribute of objects sorted by that attribute?

  • What if the attribute to be selected differs between iterators? If iterables a and b iterate over objects of type foo that have the attributes foo.x and foo.y the same type, how can you foo.x over elements a sorted by x and b sorted by y ?

For example, No. 2, if

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

then the elements must be created in order

 1, 3, 4, 6, 8 

as before. Note that the sort and result only include the x attributes from the a and y attributes from b .

+4
source share
2 answers

Tim Pitzker has already answered the case when you use the same attribute for each iterable. If you use different attributes of the same type, you can do it as follows (using complex numbers as a ready-made class that has two attributes of the same type):

In 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)) 

Since iterator returns in zip() in Python 3, we need to force it to a list before trying to index it:

 >>> # ... 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)) 
+3
source

Answer to question 1: you can provide a key attribute for sorted() . For example, if you want to sort the .name object, use

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

As for question 2: I think you will need a different attribute for each object (for example, foo.z , as suggested by Zero Piraeus), which can be accessed by sorted() , since this function cannot tell where the object is from which he is currently sorting. In the end, it gets a new iterator from chain() , which does not contain any information about whether the current element is from a or b .

+2
source

Source: https://habr.com/ru/post/1491285/


All Articles