16

I'm trying to figure out how to use bisect in a list of tuples for example

[(3, 1), (2, 2), (5, 6)]

How can I bisect this list according to the [1] in each tuple?

list_dict [(69, 8), (70, 8), ((65, 67), 6)]
tup1,tup2 (69, 8) (70, 8)
list_dict [((65, 67), 6)]
fst, snd ((65, 67),) (6,)

And I'm inserting to bisect

idx = bisect.bisect(fst, tup1[1]+tup2[1])

Which gives me unorderable types: int() < tuple()

plaes
  • 28,313
  • 10
  • 81
  • 85
user3157919
  • 163
  • 1
  • 5

5 Answers5

18

In some cases just the simple

bisect(list_of_tuples, (3, None))

will be enough.

Because None will compare less than any integer, this will give you the index of the first tuple starting with at least 3, or len(list_of_tuples) if all of them are smaller than 3. Note that list_of_tuples is sorted.

Evgeni Sergeev
  • 18,558
  • 15
  • 94
  • 112
  • 10
    that doesn't work in Python 3. If you pass the exact number 3, you'll get `TypeError: unorderable types: int() < NoneType()`. BUT `bisect(list_of_tuples, (3, ))` would be all right. – Jean-François Fabre Jan 16 '17 at 20:52
8

You can separate out the values into separate lists.

from bisect import bisect

data = [(3, 1), (2, 2), (5, 6)]
fst, snd = zip(*data)
idx = bisect(fst, 2)

Note however, that for bisect to work, your data really should be ordered...

Jon Clements
  • 124,071
  • 31
  • 219
  • 256
  • This specific method doesn't wrok for me since I'm updating the tuples everytime, I'll explain in the edit – user3157919 Jan 03 '14 at 17:20
  • @user3157919 you need to make sure what you're bisecting on, and what you're working with are separate (and the bisection be of comparable types) and put them back together later if needs be... – Jon Clements Jan 03 '14 at 17:31
  • The main advantage of using bisect is linear time search. Copying the list in a linear time fashion means that you might as well do a linear search. – Brent Jul 07 '20 at 05:16
2

Check the bottom section of the documentation: http://docs.python.org/3/library/bisect.html. If you want to compare to something else than the element itself, you should create a separate list of so-called keys. In your case a list of ints containing only [1] of the tuple. Use that second list to compute the index with bisect. Then use that to both insert the element into the original (list of tuples) and the key ([1] of the tuple) into the new list of keys (list of ints).

Thijs van Dien
  • 6,123
  • 1
  • 26
  • 44
  • The main advantage of using bisect is linear time search. Copying the list in a linear time fashion means that you might as well do a linear search. – Brent Jul 07 '20 at 05:17
  • @Brent That depends on how many times you're searching. My idea here is that you maintain two data structures to work in tandem. They might as well both start empty. – Thijs van Dien Jul 07 '20 at 17:30
1

Answers that suggest transforming the input list are defeating the purpose of bisect by converting what should be an O(log n) into an O(n) operation. A better solution is to use a view on the input:

class _list_view:
    def __init__(self, a, key):
        self.a = a
        self.key = key

    def __getitem__(self, index):
        return self.key(self.a[index])


def bisect_left(a, x, lo=0, hi=None, key=id):
    from bisect import bisect_left
    hi = hi or len(a)
    if key == id:
        return bisect_left(a, x, lo, hi)
    return bisect_left(_list_view(a, key), x, lo, hi)
Brent
  • 3,489
  • 3
  • 22
  • 55
0

I faced the same issue. I was storing list of (file_id, word_frequency) tuples and wanted to get the list sorted by second element in the tuple which is word_frequency. I did some research and found out how python compares tuples which is describe here https://howtodoinjava.com/python/compare-tuples/.

Essentially it looks at first element of two tuples and takes the smaller one. If the first elements are same then it compares second values an so on.

So what I did was exchanged the elements in tuple (word_frequency, file_id). Now I got my list sorted according to word frequency using bisect.

Hope this helps

samsri
  • 894
  • 9
  • 22