Q:

How to add items to multi-dimensional arrays with expanding values

I am working on a project and I have a list of lists containing names, monetary values, etc. I am running into trouble trying to update the individual sub-lists within the primary list when a user enters a value. 
For example, my list contains 4 rows (constant) and an in-determinant number of columns based on user entries. I am including the whole program just for reference in case there are questions about what it all looks like:
spacing = '- ' * 45  # formatting for DONOR header
data_list = [['NAMES', 'DONATION AMOUNT', 'Number of Gifts', 'Avg Gifts'],
         ['Rudolph S', 1500, 3, 0],  
         ['Josef M', 250, 5, 0],
         ['Joye A', 5000, 2, None],
         ['Joni M', 2750, 1, None],
         ['Rachelle L', 750, 3, None],
         ['Vena U', 1000, 7, None],
         ['Efrain L', 10000, 1, None],
         ['Mee H', 15000, 2, None],
         ['Tanya E', 50000, 1, None],
         ['Garrett H', 800, 2, None]] 

def addtolist():
"""Method for sending 'Thank You' messages to Donors, using names *"""

    while True:
        print("Enter the name of the person you are writing to (or enter 'list' to see a list of names or Q to quit) ")
        fname_prompt = input("First Name: ").strip().capitalize()
        if fname_prompt.upper() == "Q":
            break
        elif fname_prompt.lower() == "list":  
            if len(data_list) - 1 % 2 != 0:
                for i in range(0, int(len(data_list) - 1 / 2)):
                    cut_off = int((len(data_list)) / 2)
                    if i == 0:               
                        print(spacing)
                        print('{:>44s}'.format(str(data_list[i][0])))  
                        print(spacing)
                    elif cut_off + i >= len(data_list):
                        continue
                    else:
                        print('{:>30s}'.format(data_list[i][0]), '{:>35s}'.format(data_list[cut_off + i][0]))
            else:
                if i == 0:                                                  
                    print(spacing)
                    print('{:>20s}'.format(str(data_list[i])))              
                    print(spacing)
                else:
                    print('{:>15s}'.format(data_list[i][0]), '{:>30s}'.format(data_list[cut_off + i][0]))
        else:
            lname_prompt = input("Last Name: ").strip().capitalize()
            if lname_prompt.upper() == "Q":
                break
            elif lname_prompt.lower() == "list":
                if len(data_list) - 1 % 2 != 0:
                    for i in range(0, int(len(data_list) - 1 / 2)):
                        cut_off = int((len(data_list)) / 2)
                        if i == 0:                                          
                            print(spacing)
                            print('{:>44s}'.format(str(data_list[i][0])))  
                            print(spacing)
                        elif cut_off + i >= len(data_list):
                            continue
                        else:
                            print('{:>30s}'.format(data_list[i][0]), '{:>35s}'.format(data_list[cut_off + i][0]))
                else:
                    if i == 0:  # for each item in list / 2 (5 x)
                        print(spacing)
                        print('{:>20s}'.format(str(data_list[i][0])))  
                        print(spacing)
                    else:
                        print('{:>15s}'.format(data_list[i][0]), '{:>30s}'.format(data_list[cut_off + i][0]))
            else:
                full_name = fname_prompt + " " + lname_prompt
                if full_name != "List List" or full_name != "list ":
                    name_found = False
                    for vals in data_list:
                        if full_name in vals:
                            name_found = True
                        else:
                            name_found = False

                        if name_found is False:
                            add_name = input("That name is not in the Donor list. Do you want to add it to the list?  ").upper()
                            if add_name == "Y":                                         
                                data_list.append([full_name])
                                if len(data_list) - 1 % 2 != 0:
                                    for i in range(0, int(len(data_list) - (len(data_list) - 2) / 2)):
                                        cut_off = int((len(data_list)) / 2)
                                            if i == 0:                                          
                                                print(spacing)
                                                print('{:>44s}'.format(str(data_list[i][0])))
                                                print(spacing)
                                            elif cut_off + i >= len(data_list):
                                                 print('{:>30s}'.format(data_list[i][0]))
                                                continue
                                            else:
                                                print('{:>30s}'.format(data_list[i][0]), '{:>35s}'.format(data_list[cut_off + i][0]))
                                 else:
                                     if i == 0:  # for each item in list / 2 (5 x)
                                         print(spacing)
                                         print('{:>20s}'.format(str(data_list[i][0])))  
                                         print(spacing)
                                     else:
                                         print('{:>15s}'.format(data_list[i][0]), '{:>30s}'.format(data_list[cut_off + i][0]))

                             donation_amt = int(input("Enter in the donation amount from Donor {0}: $".format(full_name)))
                             print('{0} has donated ${1}'.format(full_name, donation_amt))
                             data_list.append(donation_amt) # difficulty HERE
                             print(data_list)

The main line(s) I am having difficult with are at the very end with a comment "difficult HERE". 
data_list.append(donation_amt) # difficulty HERE

I am trying to work this so that when the user enters a new name and new donation amount (or if they simply select an existing name and attach a donation amount to it), that the program can either append/insert the monetary value to the associated sublist (the name it is attached to). the way I have it set up now it is just appending the numerical amount onto the end of the larger list but I have been unsuccessful in attaching the value to the sublist... Has anyone done anything like this before?

A:

Two-dimensional lists in Python are merely lists of lists. Thus, each element of data_list is, itself, a list. Here is an example of accessing an element, the first element below your row of headers (thus, index 1):
>>> first_entry = data_list[1]
>>> first_entry
['Rudolph S', 1500, 3, 0]

Since data_list[1] (which we have stored as a variable called first_entry) is also a list we can access, we could access the fourth element (at index 3, since lists begin indexing at 0) of the first entry as follows:
>>> first_entry = data_list[1]
>>> fourth_element = first_entry[3]
>>> fourth_element
0

Or, more succinctly:
>>> data_list[1][3]
0

So, to begin to answer your question, if your goal was to update the donation amount of "Joye A", you would use data_list[3][1] = donation_amt. This is because Joye's entry is at index 3 of the main list and donations are recorded at index 1 of her sub list.
Unfortunately, this doesn't really solve your problem, since you want to take an arbitrary name for which to either create a new entry or update an existing entry. The real answer here is that you are using the wrong data structure. For the sake of educational value, though, I'll go ahead and describe how you could do this with your existing structure.
Using your matrix
First you would need to determine if the name already exists. As a result, it would be best to create an extra list which contains only the first column, a 1-d list. You could do this in any number of ways. I'll show it as a list comprehension:
>>> names_only = [e[0] for e in data_list]
>>> names_only
['NAMES', 'Rudolph S', 'Josef M', ...]

I won't explain this here, but there are plenty of threads explaining how list comprehensions work for any readers who aren't aware.
First, you check if the name already exists in the matrix:
>>> 'Josef M' in names_only
True

If so, you now need to find the index of the name you're looking for. Lists in Python have an index function:
>>> idx = names_only.index('Joesf M')
>>> idx
2

You now update his donation amount as described above:
>>> data_list[idx][1] = donation_amt

Now for if he wasn't in the matrix, we want to make a whole new row. Lets imagine we're processing a user named 'bob'. He's not yet in the matrix. Here you use your append function:
>>> data_list.append([bob, donation_amt, 1, None])

Where 1 and None can be replaced with whatever your default values are. Putting it all together as a function:
>>> def update_or_create(name, amt):
...   names = [e[0] for e in data_list]
...   if name in names:
...      idx = names.index(name)
...      data_list[idx][1] = amt
...   else:
...      data_list.append([name, amt, 1, None])

Should do what your asking for.
Finally
It would be better to use a different structure for this. I would propose a dict structure like:
new_structure = {NAME: {'donation': DONATION_AMT, 'num_gifts': NUM_GIFTS, 'avg_amt': AVERAGE_DONATION},...}

Without going into too much detail, following this format would allow the following function to perform the same task:
>>> def update_or_create(name, amt):
...   if name in new_structure:
...       new_structure[name]['donation'] = amt
...   else:
...       new_structure.update({name: {'donation': amt, 'num_gifts': 1, 'avg_amt': None}})

Much nicer.

