using bisect with tuples in python - python-3.x

I am trying to understand the use of bisect function in the python's bisect module and how to use it with tuples. I have seen that it takes a list and a value to find the index to place the value. In my case the value is a tuple. This is from leetcode 981 https://leetcode.com/problems/time-based-key-value-store/. A is a list of tuple and the tuple will contain (int,string) as its data.
class TimeMap(object):
def __init__(self):
self.M = collections.defaultdict(list)
def set(self, key, value, timestamp):
self.M[key].append((timestamp, value))
def get(self, key, timestamp):
A = self.M.get(key, None)
if A is None: return ""
i = bisect.bisect(A, (timestamp, chr(127)))
return A[i-1][1] if i else ""
I understand what they are trying to do for solving the question . I just want to understand why use chr(127). I tried using None but it gives an error. I am guessing that it will always take the first int value in the tuple because the chr(127) will always be unqualified somehow for bisect to accept. But I am not able to find the correct reason for this. also IDLE shows chr(127) as \x7f and found out its a non ascii character.

Python compares tuples element by element and from left to right. For example:
(1, 2) > (1, 1) is True
(1, 2) > (1, 2) is False
I guess, they use chr(127), to make the second item in the tuple larger than any other character.
(1, chr(127)) > (1, 'z') is True

finally found some understanable solution in leetcode discussion forum. all credits go to leetcode users
https://leetcode.com/slvher/ and https://leetcode.com/jcodem/
The ascii code range of lower char is [97, 122], thus chr(127) can be used to make sure the located index from bisect.bisect(A, (timestamp, chr(127))) can satisfy the condition: timestamp_prev <= timestamp. In fact, chr(ascii_v) can also be used here where ascii_v's range is [123, 127].

Related

What will be the time complexity of list(dict.keys()) in python3

I was solving an algorithm question using list(dict.keys()) it was taking a lot more time
than OrderedDict dict.popitem(0) I know it'll return generator type object we can traverse over it and get every element like range in list.
Time complexity is O(n) as you create a new list made of all elements.
If you just want the first, I suggest you to next(iter(dict)) or next(iter(dict.items())) which will bring you the first key or (key, value) pair respectively at O(1).
You can catch StopIteration to make sure the dict isn't empty or return a default value from next():
>>> def take_first(d):
... return next(iter(d), None)
...
>>> take_first({"a": 1, "b": 2})
'a'
>>> take_first({})
>>> # (None)

how to best iterate through dictionary keys and compare the values?

I am very new to Programming and only started learning Python 3 about 2 wks ago.
Doing an exercise that I found rather difficult, that is designed to create a function that accepts a dictionary as an argument and is supposed to determine if the dictionary represents a "valid" chessboard. Plz note the following codes only address a single aspect of the function. The part I had the greatest struggle with.
I spent quite a bit of time working on this particular project and trying to insure that both options are "valid" code so afaik there are no errors in either?
Imagine a grid (I will print the list) that is supposed to represent the squares on a chessboard. Could someone tell me which code would be deemed as more acceptable? and Why? Or if there is a simpler way I could have done this? I will only post what I feel is "relevant" to my question if more is needed plz lmk.
checks that dictionary keys are valid Chessboard Squares
# acceptable range is columns 1 - 8 rows a - h
for board_squares in dic:
try: # this will accept any value as int
if (int(board_squares[0:-1]) <= 8 # as slice up to last key char
and board_squares[-1] <= 'h') \
is False:
print((Err) + ' Square outside range')
return False
except ValueError as e:
print((Err) + ' Improper dictionary')
return False # when testing function this can be replaced with return False
Important note: In this occurrence I am referring to "board_squares" as the dictionary keys. This is the first code I came up with after a lot of effort. It slices the dictionary key and compares it to what is supposed to be represent a "valid" chessboard square. I got a bit of negative feedback on it so I went back to the drawing board and came up with this code:
def char_range(c1, c2):
"""Generates the characters from `c1` to `c2`, inclusive."""
for c in range(ord(c1), ord(c2)+1):
yield chr(c)
chessboard_squares = []
for chr1 in range(1, 9):
for chr2 in char_range('a', 'h'):
chessboard_squares.append(str(chr1) + chr2)
print(chessboard_squares) # this is simply to print list so I have a visual representation
for key in dic:
if key in list \
is False:
print((Err) + ' Square outside range')
return False
Important note: In this occurrence I am referring to chessboard_squares as values in the list that the dictionary keys are compared to. This second code requires the function at the top to range over letters. I tried to insure it was very readable by using clearly defined variable labels. It creates a list of what the "valid dictionary keys should be" to represent Chessboard Squares. And lastly here is the printed list of what the valid dictionary keys "should be". Post is in the format of chessboard squares for clarity.
['1a', '1b', '1c', '1d', '1e', '1f', '1g', '1h',
'2a', '2b', '2c', '2d', '2e', '2f', '2g', '2h',
'3a', '3b', '3c', '3d', '3e', '3f', '3g', '3h',
'4a', '4b', '4c', '4d', '4e', '4f', '4g', '4h',
'5a', '5b', '5c', '5d', '5e', '5f', '5g', '5h',
'6a', '6b', '6c', '6d', '6e', '6f', '6g', '6h',
'7a', '7b', '7c', '7d', '7e', '7f', '7g', '7h',
'8a', '8b', '8c', '8d', '8e', '8f', '8g', '8h']
Since I posted this question I've learned a lot of new things and decided to answer my own question. Or if there is a simpler way I could have done this? Here is a much cleaner, and should be considered the "best", option.
try:
if all (
(1 <= int(row) <= 8) and ('a' <= col <= 'h')
for row, col in dict
):
return True
except ValueError:
return False
First we use the all() function that takes ALL the arguments passed to it and returns True if all are True. Empty strings count as a special exception of True.
All our dictionary keys are (supposed to be) 2 character strings which are in themselves iterable, and I can use multiple assignment(aka tuple unpacking) here if I assign exactly as many characters as are in the dictionary key to variables. In this case we assign the 1st char of the dictionary key to row and the 2nd char of the dictionary key to col(umn). I can still use try/except ValueError because if the dictionary key isn't exactly 2 characters it will raise the same error and I am checking for specific keys.
A simple understanding short version of a list or generator "comprehension" is doSomething for variable in iterable this is a "generator comprehension". What we end up with is:
Do something: cmp int(row) to 1 - 8 and col 'a' - 'h'
for: row(1st char of dict key), col(2nd char of dict key)
in: dictionary keys.
Because this is a "generator comprehension" it will create a set of values based off each loop iteration. and as an example might look something like this: True, False, False, True etc.
These values will in turn be passed to all() that will consume them and return True if ALL are True else False.
here are several resources to help understand the code should anyone wish to look further:
the all function:
https://docs.python.org/3/library/functions.html#all
understanding list comprehension:
https://medium.com/swlh/list-comprehensions-in-python-3-for-beginners-8c2b18966d93
this is great in that it explains "yield" which is vital in understanding generator comprehension:
What does the "yield" keyword do?
Multiple Assignment:
https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/

For loop list not updating

This should be a simple problem however at the moment I cannot understand why. Below is a simple code that is suppose to stripe of all strings and convert to int. However the results do not agree with what I wrote.
num = ('"28"', '"23"', '"35"', '"50"', 29488)
for i in num:
if type(i) is str:
i = i[1:-1]
print(i)
print(num)
Expected output
28
23
35
50
(28, 23, 35, 50, 29488)
Actual output
28
23
35
50
('"28"', '"23"', '"35"', '"50"', 29488)
Just found out I had a tuple, when I thought it was a list...
There are a few problems to address
Strings are immutable, so you're not really changing anything in the list, you're just reassigning the loop variable
You're not converting anything to ints, only removing characters from strings
If you want to be able to reassign elements within the nums, you need to use an actual list variable
For example
num = ['"28"', '"23"', '"35"', '"50"', 29488]
for i, n in enumerate(num):
if isinstance(n, str):
num[i] = int(n.strip('"'))
print(n)
print(num)
Re-assignment is missing to index position
Tuples are immutable, can not be set values using index position.
You can refer answer given by #cricket_007, also if you want single liner list compression way then refer below -
num = tuple(int(n.strip('"')) if isinstance(n, str) else n for n in num)
Then you will get again new tuple with values converted into int.

Duplicate tuples inside a tuple

I am working on a project for university and I am stuck.
There is a tuple made out of several positions, these positions being represented as tuples.
So, let's call this tuple "positions".
positions = ((2, 1), (2, 2), (1, 1), (2, 1))
This would be an example of what positions could be in the tuple.
I am supposed to check if any of the position (small tuples) is being repeated in the tuple presenting all position (big tuple), resulting in a False output.
In this example, there is a position that is being repeated.
I tried using for loops. I really have no clue on how else to do it.
def positions_func(positions):
for i in range(len(positions)):
for j in range(len(positions)):
if positions[i] == positions[:j]:
return False
The error coming out is that the tuple is out of the index, proving that I am doing it wrong.
Two easy ways, depending on what you need to do next one may be better than the other.
One, turn the big tuple into a set and compare their lengths:
if len(positions) != len(set(positions)):
print("There were duplicates.")
Or with collections.Counter, e.g. if you need to know which one was duplicate:
from collections import Counter
counts = Counter(positions)
for item, count in counts.most_common():
print(item, "occurred", count, "times.")
if count > 1:
print("(so there was a duplicate)")
I think it's happening because of the colun you added with the j index.
def positions_func(positions):
for i in range(len(positions)):
for j in range(len(positions)):
if positions[i] == positions[j] and i != j:
return False
Try th above code. It will check if two tuples are identical and return false

Getting a list item with the max evaluation in a list of tuples in Python

Given this list of tuples:
my_tuples = [(1,2), (3,4)]
and the following evaluation function:
def evaluate(item_tuple):
return item_tuple[0] * 2
Question: how can I get the list item (tuple) that has the highest evaluation value? (I'm guessing I can use a list comprehension for this)
def max_item(tuples_list, evaluation_fn):
'''Should return the tuple that scores max using evaluation_fn'''
# TODO Implement
# This should pass
assertEqual((3,4), max_item(my_tuples, evaluate))
Correct me if I'm wrong, you want the list of tuples sorted by the result of multiplying one of the values inside the tuple with x (in your example above it would be the first value of the tuple multiplied by 2).
If so, you can do it this way:
from operator import itemgetter
sorted(l, key=itemgetter(0 * 2), reverse=True)
I managed to do it this way:
def max_item(tuples_list, evaluation_fn):
zipped = zip(map(evaluation_fn, tuples_list), tuples_list)
return max(zipped, key=lambda i:i[0])[1]
I don't know if there's a simpler (more pythonic?) way to solve it though.
Edit
I figured how I could use a list comprehension to make it more succinct/readable:
def max_item(tuples_list, evaluation_fn):
return max([(evaluation_fn(i), i) for i in tuples_list])[1]

Resources