1

I am a beginner at Python, and will appreciate some explanation on the faults in my attempts. I'd also appreciate some help on how to proceed from here. I'll link the challenging statements first, and the verbose code block after.

I have defined a dictionary: loc_dictionary to receive key-value pairs. The key is a user-inputted name and the value is a list of two values to represent latitude and longitude, also inputted by the user.

I am trying to find the difference between the latitude in two dictionary keys in a loop:

       deltalat = loc_dictionary[i + 1][0] - loc_dictionary[i][0]
       print(deltalat)

The above code is meant to access the variable in the next iteration minus the variable in the current iteration of the dictionary. It tells me that I cannot concatenate the string to int ([i + 1]). TypeError: can only concatenate str (not "int") to str

Then, I tried another method using nested for loops:

for i in range(0, len(loc_dictionary)):
    for j in range(1, len(loc_dictionary)):
        deltalat = loc_dictionary[j][0] - loc_dictionary[i][0]
    print(deltalat)

This throws the following error:

deltalat = loc_dictionary[j][0] - loc_dictionary[i][0]
KeyError: 1

Which I believe means that the dictionary key being referenced cannot be found. Using Jupyter, I got some more help on an error telling me dictionary keys do not support indexing. What should I do next? Kindly find the complete code below:

loc_dictionary = {}


# Method for accepting coordinates
def route():
    count = 0
    primary_loc_key = input('Please type in the name of first location.\n>>> ').strip()
    primary_loc_value = [
        eval(input('Please type in the latitude  of \'' + primary_loc_key + '\'.\n>>> ').strip()),
        eval(input('Please type in the longitude  of \'' + primary_loc_key + '\'.\n>>> ').strip())]
    loc_dictionary[primary_loc_key] = primary_loc_value
    location_exit_loop = 'done'
    loc_input_key = ''
    loc_input_value = []
    print('<<<Type \'Done\' as the location name when all locations have been inputted.>>>')
    while loc_input_key.strip().lower() != location_exit_loop:
        loc_input_key = input('Please type in the name of the ' + str(count + 1) + ' stop.\n>>> ').strip()
        if loc_input_key.strip().lower() != location_exit_loop:
            loc_input_value = [eval(input('Please type in the latitude of ' + loc_input_key + '.\n>>> ').strip()),
                               eval(input('Please type in the longitude of ' + loc_input_key + '.\n>>> ').strip())]
        else:
            loc_input_value = None
        loc_dictionary[loc_input_key] = loc_input_value
        count += 1
    if (count - 1) < 2:
        print('You have a single stop.')
    else:
        print('You have ', count - 1, 'stops to be calculated.')
    del loc_dictionary['done']
    print(loc_dictionary)
    return loc_dictionary


# Calculate through loop
def coordinates():
    latitude = 0
    longitude = 0
    # loop through items in the loc_dictionary
    for i in loc_dictionary:
        latitude = loc_dictionary[i][0]
        longitude = loc_dictionary[i][1]
        print(i + '\nLatitude: ' + str(latitude) + '\nLongitude: ' + str(longitude))

    # for i in loc_dictionary:
    #   deltalat = loc_dictionary[i + 1][0] - loc_dictionary[i][0]
    # print(deltalat)
    for i in range(0, len(loc_dictionary)):
        for j in range(1, len(loc_dictionary)):
            deltalat = loc_dictionary[j][0] - loc_dictionary[i][0]
        print(deltalat)

route()
coordinates()
Vyz Ejstu
  • 25
  • 6

2 Answers2

2

Your first error

TypeError: can only concatenate str (not "int") to str

is probably being given because you are storing the numbers for your latitude & longitude values as strings. You can convert these to numbers so they can be added together using float() eg

float(latitude1) - float(latitude2)

as for accessing the dictionary, the easiest way to iterate through a dict is like this:

for each key, value in dictionary:
    print(key)
    print(value)

Note that dictionaries in python are not normally ordered, however, so you are better off using an array, ie changing your code where you put the data from:

loc_dictionary[loc_input_key] = loc_input_value

to

loc_array += [[loc_name, loc_longitude, loc_latitude]]

and instead of assigning an empty value to the key 'done' and then deleting it, put the assignment behind an if statement that checks that the location name is not 'done' Using an array will then allow you to loop through it as you are expecting to be able to in your code.


edit:

Looking at your code in more detail, you are correct in your comment that your numbers are stored as numbers. However, on your first loop attempt you have looped as so:

for i in loc_dictionary:

This is an iteration through the keys of the dictionary, and so i is a string, not an integer. Therefore you can't add 1 to it to try and get the next iteration. You should be able to see this as the previous loop will output the key along with the location numbers, not an integer

I've also updated my code so that the array assign actually works

Thering
  • 101
  • 5
  • Thanks for your help. Thering. Unfortunately, it didn't work. If you look closely, the input for latitude take on the type that is entered. I encapsulated the input with `eval`. The problem with the string concatenation lies with the way I'm trying to access the next item in the dictionary: `loc_dictionary[i + 1][0]`. Using an array as you suggested, didn't work either. I can't seem to find a way to append the input through looping. The code you suggested replaces the values in the array with the input every time the loop runs – Vyz Ejstu Jan 30 '20 at 12:14
  • In Python 3.7, ordered dictionaries are now a standard feature. How do I use that to my advantages when accessing positions in a list within a dictionary value? Thanks in advance for your help. Regards. – Vyz Ejstu Jan 30 '20 at 12:34
  • What version of python is your Jupyter running? go to help->about to see. Mine is 3.6.7 – Thering Feb 06 '20 at 12:58
  • Thanks. I found a different solution that I intend to post later. I'm not running Jupyter, I'm using PyCharm. The version is 3.8. – Vyz Ejstu Feb 11 '20 at 13:18
  • Ok. You did mention Jupyter in your question, hence I asked – Thering Feb 11 '20 at 18:00
0

I had to use @nosklo's solution which you can find here:

The first thing to be done was to change the loc_dictionary variable from a dictionary to a list as @Thering suggested:

loc_dictionary = []
primary_loc_list = []

Then, I type the new method:

    # Method for accepting coordinates
def accept_coordinates():
    count = 0
    primary_loc_list.append(input('Please type in the name of first location.\n>>> ').strip())
    primary_loc_list.append(eval(input('Please type in the latitude of \'' + primary_loc_list[0] + '\'.\n>>> ').strip()))
    primary_loc_list.append(eval(input('Please type in the longitude of \'' + primary_loc_list[0] + '\'.\n>>> ').strip()))
    # [[location_name, latitude, longitude]]
    loc_dictionary.append(primary_loc_list)
    location_exit_loop = 'done'
    loc_input_key = ''
    print('<<<Type \'Done\' as the location name when all locations have been inputted.>>>')
    while loc_input_key.strip().lower() != location_exit_loop:
        # for every time the loop runs, reset other_loc_list to empty
        other_loc_list = []
        loc_input_key = input('Please type in the name of the ' + str(count + 1) + ' stop.\n>>> ').strip()
        if loc_input_key.strip().lower() != location_exit_loop:
            other_loc_list.append(loc_input_key)
            if loc_input_key.strip().lower() != location_exit_loop:
                other_loc_list.append(eval(input('Please type in the latitude of ' + loc_input_key + '.\n>>> ').strip()))
                other_loc_list.append(eval(input('Please type in the longitude of ' + loc_input_key + '.\n>>> ').strip()))
            else:
                loc_input_value = None
            # append all the values in other_loc_list as a list to the loc_dictionary before running the loop again.
            loc_dictionary.append(other_loc_list)
        else:
            break
        count += 1
    if count < 2:
        print('You have a single stop.')
    else:
        print('You have ', count, 'stops to be calculated.')
    return loc_dictionary

A dictionary key and corresponding list as the dictionary value didn't work. I found it better to create a new input list every loop for the name, latitude and longitude and append that to the master list:

while loc_input_key.strip().lower() != location_exit_loop:
        # for every time the loop runs, reset other_loc_list to empty
        other_loc_list = []
        loc_input_key = input('Please type in the name of the ' + str(count + 1) + ' stop.\n>>> ').strip()
        if loc_input_key.strip().lower() != location_exit_loop:
            other_loc_list.append(loc_input_key)
            if loc_input_key.strip().lower() != location_exit_loop:
                other_loc_list.append(eval(input('Please type in the latitude of ' + loc_input_key + '.\n>>> ').strip()))
                other_loc_list.append(eval(input('Please type in the longitude of ' + loc_input_key + '.\n>>> ').strip()))
            else:
                loc_input_value = None
            # append all the values in other_loc_list as a list to the loc_dictionary before running the loop again.
            loc_dictionary.append(other_loc_list)

Now, the function below takes an argument. That argument will be the list we want to get the values from. prevs appends the argument to None. This is because the previous element at the start of a list doesn't exist. nexts slices the argument and appends None to the end. The next item at the end of a list doesn't exist.

# function to get previous, next and current items in a list.
def next_and_previous(x):
    prevs, item, nexts = tee(x,3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return zip(prevs, item, nexts)

In essence, it appears to divide a single list into three per iteration: prevs will serve the function of the previous list item, item is the current item, and nexts serves the function of the next item in the list.

Please see nosklo's answer for a proper explanation.

The function below is responsible for getting the variables and working on them:

# Calculate through loop
def calculate_distance():
    count = 0
    total_distance = 0
    # loop through items in the loc_dictionary
    for i in loc_dictionary:
        latitude = i[1]
        longitude = i[2]
        print('\n' + i[0] + '\nLatitude: ' + str(latitude) + '\nLongitude: ' + str(longitude))

    for prev, item, nxt in next_and_previous(loc_dictionary):
        if count != len(loc_dictionary):
            count += 1
            # print('The next item is', nxt, 'and the current item is', item, 'and the previous item is', prev)
            try:
                lat2 = math.radians(nxt[1])
                lat1 = math.radians(item[1])
                lon2 = math.radians(nxt[2])
                lon1 = math.radians(item[2])

...

Nesting lists allows the program to access a particular index within a list:

# loop through items in the loc_dictionary
    for i in loc_dictionary:
        latitude = i[1]
        longitude = i[2]

Here is the implementation of the next_and_previous() function:

for prev, item, nxt in next_and_previous(loc_dictionary):
        if count != len(loc_dictionary):
            count += 1
            # print('The next item is', nxt, 'and the current item is', item, 'and the previous item is', prev)
            try:
                lat2 = math.radians(nxt[1])
                lat1 = math.radians(item[1])
                lon2 = math.radians(nxt[2])
                lon1 = math.radians(item[2])

Python allows a program to operate on called variables directly in some situations. lat2 is the next item at index 1 of the list. lat1 is the current item at index 1 one of the list. Remember, the name of the location is stored at index 0. We don't need to operate on that. lon2 and lon1 do the same as above for the items at index 2.

Vyz Ejstu
  • 25
  • 6