User Input, SearchCursor, and for loop?

715
9
11-28-2017 04:50 PM
StephanieBurnett
New Contributor

I'm working on a project in which I'm trying to utilize user input in order to search an attribute table for a city. If the city is not present in the table, I want to have a secondary response. So far, I've been using a searchCursor followed by a for loop and then an if statement. If I use a single If statement, I am able to achieve a single response. But if I include a second conditional statement, be it an elif an else or a second if, the script goes through the whole table and gives me a response for each row. Ideally, I would like it to give me a single response (ie. "Yes, its in the table", "No its not in the table"). I'm relatively new to arcGIS, and even newer to Python. Below is my sample text. Any help would be greatly appreciated.

#import necessary tools
import arcpy
from arcpy import env
import sys

#set workspace
arcpy.env.workspace = r'C:\Data' #generic source for data

#inquire user input
print "Please search for a city in New Mexico within 10 Miles of an Amtrak Station."
userInput = raw_input ("Which city would you like to check? ")

#establish the SearchCursor
citySearch = arcpy.da.SearchCursor('CitiesAmtrak.shp', 'NAME')

#create a loop to utilize SearchCursor and provide feedback
for column in citySearch:
    if userInput in column:
        print "This city is within 10 miles of an Amtrak Station."‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
        break
    if userInput not in column:
        print "no"
  
0 Kudos
9 Replies
DanPatterson_Retired
MVP Esteemed Contributor

line 21&22

else:

    print("no")

you don't double check with 2 if statements

0 Kudos
StephanieBurnett
New Contributor

Even with an else statement, I still receive "no" returned for as many rows before the correct input, or for all the rows if the input is not in the table.

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

Oh I get it now.  That is how search cursors work, one row at a time.  You are looking for something that vectorizes stuff behinds the scenes.  It exists (numpy, scipy) but the scene behind those scenes are the same just faster

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

_is_in(a, 3)  # a function equivalent to for and if loop, but doesn't contain for or if

array([False, False, False,  True, False, False, False, False, False, False], dtype=bool)

If depends on what is 'exposed'  The above is a simple function (that doesn't contain a for or if) which returns the result seemingly automagically.  

0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor
# how about....
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

for i in a:
    if i == 3:
        val = 'found'
        break
    else:
        val = 'not found'
print(result.format(val))

# result

The value is found
0 Kudos
StephanieBurnett
New Contributor

Thank you for your help, I really appreciate it. The script now will only give that the value is not found as an output. Regardless of whether the value should be found or not.

#inquire user input
print "Please search for a city in New Mexico within 10 Miles of an Amtrak Station."
userInput = raw_input ("Which city would you like to check? ")

#establish the SearchCursor
citySearch = arcpy.da.SearchCursor('CitiesAmtrak.shp', 'NAME')
#create a loop to utilize SearchCursor and provide feedback
for row in citySearch:
    if row == userInput:
        val = 'found'
        break
    else:
        val = 'not found'
print "The Value is {1}.".format(val)
0 Kudos
DanPatterson_Retired
MVP Esteemed Contributor

Oooo ugly text and case testing.  IF you are working with text entry, you had better case everything to upper or lower case prior to doing the testing... and little things like a stray blank space at the beginning or end spell toast for any well intentioned programmer.  And I am assuming that people know how to spell and a hundred other things.  So make sure you test with something that is known

a = ['a', 'b', 'bb', 'A', 'Aa']
for i in a:
    if i.upper() == 'B':
        val = 'found'
        break
    else:
        val = 'not found'
print("value {}".format(val))

value found
RandyBurton
MVP Regular Contributor

Assuming you are looking for an exact match, you might try:

fc = 'CitiesAmtrak.shp'
field = 'NAME'
whereClause = "{} = '{}'".format(field,userInput)

if len(list(i for i in arcpy.da.SearchCursor(fc, [field],where_clause=whereClause))):
    print "Found"
else:
    print "Not found"‍‍‍‍‍‍‍‍

Using fuzzy might offer other options, such as "Did you mean ....".

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

Not addressing the equality checking issues people have raised, the else clause on the for statement was created for this type of situation:

#import necessary tools
import arcpy
from arcpy import env
import sys

#set workspace
arcpy.env.workspace = r'C:\Data' #generic source for data

#inquire user input
print "Please search for a city in New Mexico within 10 Miles of an Amtrak Station."
userInput = raw_input ("Which city would you like to check? ")

       
with arcpy.da.SearchCursor('CitiesAmtrak.shp', 'NAME') as citySearch:
    for column in citySearch:
        if userInput in column:
            print "This city is within 10 miles of an Amtrak Station."
            break
    else:
        print "no"
XanderBakker
Esri Esteemed Contributor

I would probably make a list of the cities and then check if there is an "exact" match (ignoring case) and if not see if the user input is contains by any of the names: 

def main():
    import arcpy

    # input featureclass and field
    fc = r'C:\path\to\folder\CitiesAmtrak.shp'
    fld_name = 'NAME'

    # create a list with uppercase names
    names = [r[0].upper() for r in arcpy.da.SearchCursor(fc, (fld_name))]

    user_input = (raw_input("Which city would you like to check? ")).upper()

    if user_input in names:
        print("City found (exact match, ignoring case)")
    else:
        test = [name for name in names if user_input in name]
        print test
        if len(test) == 1:
            print("1 City found that contains with user input: {0}".format(test[0]))
        elif len(test) > 1:
            print("{0} Cities found that contain with user input ({1})".format(len(test), ", ".join(test)))
        else:
            print("No cities found...")


if __name__ == '__main__':
    main()‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

If I test with a list of Amtrak cities:

    names = [name.upper() for name in ['Albany', 'Flagstaff', 'Chicago', 'Glenwood Springs', 'Philadelphia',
             'Saratoga Springs', 'New York City', 'Eugene', 'Seattle', 'Dallas', 'Albuquerque',
             'Washington, DC', 'Los Angeles', 'San Antonio', 'St. Louis',
             'Emeryville', 'Kansas City, MO', 'Port Huron', 'Tucson', 'Denver',
             'Vancouver, BC', 'Montreal', 'New Orleans', 'Kalamazoo', 'East Lansing',
             'Battle Creek', 'Boston', 'Portland', 'Tacoma']]

... and test for "new", it will print:

2 Cities found that contain with user input (NEW YORK CITY, NEW ORLEANS)‍

and with "York":

1 City found that contains with user input: NEW YORK CITY

and with "new york city":

City found (exact match, ignoring case)

There is something to keep in mind (apart of how to handle each case towards the user). In case you identified the city you may need the exact name to be able to do something with the result. Because,if your city is called "New York City" and the user specified "new york city" it will not match with the city you are looking for.

In order to do that you could consider creating a dictionary that holds the uppercase of the name as key and the original name as the value:

def main():
    import arcpy

    # input featureclass and field
    fc = r'C:\path\to\folder\CitiesAmtrak.shp'
    fld_name = 'NAME'

    # create a list with uppercase names
    dct_names = {r[0].upper(): r[0] for r in arcpy.da.SearchCursor(fc, (fld_name))}
    names = dct_names.keys()

    user_input = (raw_input("Which city would you like to check? ")).upper()
    print user_input

    if user_input in names:
        print("City found (exact match, ignoring case)")
    else:
        test = [dct_names[name] for name in names if user_input in name]
        print test
        if len(test) == 1:
            print("1 City found that contains with user input: {0}".format(test[0]))
        elif len(test) > 1:
            print("{0} Cities found that contains with user input ({1})".format(len(test), ", ".join(test)))
        else:
            print("No results found...")


if __name__ == '__main__':
    main()