2

I want to get the minimal element of a list of list of tuples

a = [[(1, 0), (2, 0), (1, 1)], [(2, 0), (1, 1), (1, 0)], [(1, 1), (1, 0), (2, 0)]]

in lexicographic order, so that [(1,1),(1,0),(2,0)]] < [(1,0),(2,0),(1,1)] , since the 0-th entry of the tuple has the greater priority, that is 1,1,2 < 1,2,1, and the 1-st entry has less priority.

min(a)

returns [(1, 0), (2, 0), (1, 1)], which of course is not correct.

I only need the index of the minimal element, so an incorrect version would be

print(min(range(len(a)), key=lambda i: a[i]))

(both minimal element and index only methods would be appreciated).

Of course one could write a custom loop with zip or something, but I would like a solution with little overhead.

Jaka Belec
  • 115
  • 4

2 Answers2

5

You can use a custom key, zipping the tuples (zip):

min(a, key=lambda x: list(zip(*x)))

Output: [(1, 1), (1, 0), (2, 0)]

how it works

The default comparison of lists of tuples works by comparing the first tuples, then the second, etc. (sort of depth first while you want breadth first).

Since you want priority on the first item of each tuple, you need to reorganize the tuples. Internally, with this lambda x: list(zip(*x)) key, min sees the items this way:

[list(zip(*x)) for x in a]
# [[(1, 2, 1), (0, 0, 1)], [(2, 1, 1), (0, 1, 0)], [(1, 1, 2), (1, 0, 0)]]

And their sorted order is:

[[(1, 1, 2), (1, 0, 0)], [(1, 2, 1), (0, 0, 1)], [(2, 1, 1), (0, 1, 0)]]
mozway
  • 194,879
  • 13
  • 39
  • 75
  • This is interesting. Could you add some commentary to explain how it works? – ciaran haines Apr 17 '23 at 17:57
  • 1
    @ciaranhaines sure I added some details, let me know if you need more – mozway Apr 17 '23 at 18:01
  • Couldn't you use `next` instead of `list` here? Like so: `min(a, key=lambda x: next(zip(*x)))` Just a minor improvement over evaluating the entire zip object to a list – Jab Apr 17 '23 at 18:06
  • @Jab OP said "less priority", not "no priority". – Kelly Bundy Apr 17 '23 at 18:11
  • @Jab actually if you try on `b = [[(1, 0), (2, 0), (1, 1)], [(2, 0), (1, 1), (1, 0)], [(1, 1), (1, 0), (2, 1)], [(1, 1), (1, 0), (2, 0)]]` this will give an incorrect sorting – mozway Apr 17 '23 at 18:27
0

You could use a sort key that picks up only the first element of each tuple:

sorted(a,key=lambda L:[k for k,_ in L]) # effective ordering

[[(1, 1), (1, 0), (2, 0)], [(1, 0), (2, 0), (1, 1)], [(2, 0), (1, 1), (1, 0)]]

min(a,key=lambda L:[k for k,_ in L]) # lowest/first value in that order

[(1, 1), (1, 0), (2, 0)]
Alain T.
  • 40,517
  • 4
  • 31
  • 51