Search code examples
pythondictionaryfloating-pointinteger

How to search for a key that is a float using an int that the float would round to


I have two lists that represent the x and y coordinates as a plane, and each point on the plane has a random number assigned to it which i use as a key to search for the coordinates, the problem is i need there to be a chance that the random number assigned to a point is a duplicate so i used float but now i need to search through the dictionaries using an int as the key to see if that number exists and what point it is assigned to. So essentially I'm wondering if it's possible to search for the rounded version of a key for example if the key is 10.3 i need to find it using an int equal to 10.

x_amount = int(input("Height of room: "))
y_amount = int(input("Width of room: "))
# Asks for height and width of plane

Room_Total = x_amount * y_amount
numbers = []
length = len(str(Room_Total)) + 1
x_values = {}
y_values = {}
create_number = y_amount

def num_find(num):
    y_coord = y_values[num]
    x_coord = x_values[num]
    # Here is where i want to search for the coordinates using an int

for num in range(1, (y_amount + 1)):
    for n in range(1, (x_amount + 1)):
        number = 1
        while number != 0:
            number = random.uniform(1, Room_Total)
            if number not in numbers:
                numbers.append(round(number))
                x_values[number] = num
                y_values[number] = n
                number = 0
# Randomly generates a number for every place in the grid
      
plane = input("Want to see the plane? y/n ")
if plane == "y":
    for i in numbers:
        num_gap = length - len(str(i))
        create_number = create_number - 1
        if create_number == -1:
            print("")
            create_number = y_amount - 1
            print(i,end="  ")
            for num in range(1, num_gap):
                print(" ", end="")
       else:
           print(i,end="  ")
           for num in range(1, num_gap):
               print(" ", end="")
# Prints grid if the user wants it (So that if the grid is 100 by 100 for example it doesn't print it)

I searched for similar problems that have an answer but haven't quite found anything that applies to this situation and haven't been sure what else to try.


Solution

  • [If I understand correctly, you want help with the num_find function.] An integer num might have multiple float keys that round to it, so you can return a list of coordinates for keys that round to num using list comprehension.

    def num_find(num):
        knums = [k for k in x_values if round(k)==num] ## random `number`s that round to num
        return [{'value':k, 'x':x_values[k], 'y':y_values[k]} for k in knums] 
    

    With the 5x5 plane described below:

    numbers = [22, 17, 17, 1, 7, 5, 23, 4, 21, 15, 7, 17, 12, 22, 12, 6, 24, 17, 7, 3, 11, 3, 9, 10, 16]
    x_values = { 21.56616443204579: 1, 16.88672504030793: 1, 16.61564085529154: 1, 1.4623292817213773: 1, 7.186315822551395: 1,  5.387703554349793: 2, 23.004220335362547: 2, 4.30105672619399: 2, 20.7082214032309: 2, 15.161515180582256: 2, 7.038981849299793: 3, 17.32470245778697: 3, 11.939846516953184: 3, 
                 21.985484460089975: 3, 12.186946846244577: 3, 5.953912098756866: 4, 24.077177195496617: 4, 16.932061089221378: 4, 6.965151311285224: 4, 2.8087793024821837: 4, 10.66278368789144: 5, 3.2461449455136293: 5, 8.615396657669486: 5, 9.667998604001081: 5, 15.636821583006896: 5 }
    y_values = { 21.56616443204579: 1, 16.88672504030793: 2, 16.61564085529154: 3, 1.4623292817213773: 4, 7.186315822551395: 5, 5.387703554349793: 1, 23.004220335362547: 2, 4.30105672619399: 3, 20.7082214032309: 4, 15.161515180582256: 5, 7.038981849299793: 1, 17.32470245778697: 2, 
                 11.939846516953184: 3, 21.985484460089975: 4, 12.186946846244577: 5, 5.953912098756866: 1, 24.077177195496617: 2, 16.932061089221378: 3, 6.965151311285224: 4, 2.8087793024821837: 5, 10.66278368789144: 1, 3.2461449455136293: 2, 8.615396657669486: 3, 9.667998604001081: 4, 15.636821583006896: 5 }
    
    • num_find(7) would return
      [ {'value': 7.186315822551395, 'x': 1, 'y': 5},
        {'value': 7.038981849299793, 'x': 3, 'y': 1},
        {'value': 6.965151311285224, 'x': 4, 'y': 4} ]
      
    • num_find(5) would return [{'value': 5.387703554349793, 'x': 2, 'y': 1}], and
    • num_find(29) would return [] (an empty list because there would be no matches)

    On another note,

    1. It's a bit redundant to have two separate dictionaries (x_values and y_values) with the exact same set of keys. It might be better to have a dictionary of tuples instead. Instead of x_values[number]=num and y_values[number]=n (in if number not in numbers...), you can just have one dictionary xy_values that you fill up with xy_values[number] = (num, n).

    2. Since number is a float and numbers is being built as a list of integers, it's somewhat superfluous to check for number not in numbers:

                  if number not in numbers:
                      numbers.append(round(number))
      

      I know you can't use if round(number) not in numbers: since you want duplicate integers, but this is only checking against duplicate whole-number keys. The chances of repeating a "random" float (without specifying a limit on the decimal places) is miniscule, but if you're checking against duplicate keys (which you should be, if you don't want to risk losing any coordinates by over-writing them), you should be checking against all of them, not just whole numbers.

    I would suggest filling up your "plane" with something like:

    xy_values = {} ## instead of `x_values={}` and `y_values={}`
    
    for num in range(1, (y_amount + 1)):
        for n in range(1, (x_amount + 1)):
            number = random.uniform(1, Room_Total)
            while number in xy_values: ## [ `in xy_values` <===> `in xy_values.keys()` ]
                number = random.uniform(1, Room_Total)
            numbers.append(round(number))
            xy_values[number] = (num, n) ## [ only one dictionary to fill ]
    

    While numbers would be unaffected by this change, xy_values for the plane I described as an example above would look like:

    # xy_values = \
    { 21.56616443204579: (1, 1), 16.88672504030793: (1, 2), 16.61564085529154: (1, 3), 1.4623292817213773: (1, 4), 7.186315822551395: (1, 5),
      5.387703554349793: (2, 1), 23.004220335362547: (2, 2), 4.30105672619399: (2, 3), 20.7082214032309: (2, 4), 15.161515180582256: (2, 5),
      7.038981849299793: (3, 1), 17.32470245778697: (3, 2), 11.939846516953184: (3, 3), 21.985484460089975: (3, 4), 12.186946846244577: (3, 5),
      5.953912098756866: (4, 1), 24.077177195496617: (4, 2), 16.932061089221378: (4, 3), 6.965151311285224: (4, 4), 2.8087793024821837: (4, 5),
      10.66278368789144: (5, 1), 3.2461449455136293: (5, 2), 8.615396657669486: (5, 3), 9.667998604001081: (5, 4), 15.636821583006896: (5, 5) }
    

    and num_find could just be defined as

    def num_find(num): 
        return [{'value':k, 'x':x, 'y':y} for k,(x,y) in xy_values.items() if round(k)==num] 
    

    [It will produce the same results as before, but will be a bit more efficient since there's only one loop (for going through one dictionary) rather than two loops where you have to loop through a dictionary to gather keys that match, and then loop through the matched keys and fetch values from two separate dictionaries...]


    In addition, I recommend looking into f-strings and the built-in enumerate function, and maybe into conditional expressions as well, because your if plane == "y":... block can be reduced to:

    # plane = input("Want to see the plane? y/n ")
    if plane == "y": 
        for n, i in enumerate(numbers, 1):
            print(f'{i:<{length}}', end=' ' if n%y_amount else '\n') 
    

    [You can use if n%y_amount instead of if n%y_amount!=0 as 0 evaluates as False.]