2

I'm being tasked for developing a script tool that can find ultimately needs to be able to find any string and replace it with another string in an attribute table. Below is what I have so far, and it seems to be on its way to working except but in a very strict and limited way.

Some ways I want/need to spruce it up include:

  1. Being able to skip over OBJECTID and SHAPE fields so they cannot be changed...I figure this could be achieved by having a parameter that allows several entries, but I'm not sure how that would work. I found some sort of ".split(;)" thing to attach to the targetFields parameter earlier today but it didn't seem to cooperate.
  2. Being able to find and replace partial strings
  3. I would also like the script to be able to handle integers and floats, AND I would like radio buttons in the final tool that would let me select between strings, ints, and floats.
import arcpy

FC = arcpy.GetParameterAsText(0)
oldText = arcpy.GetParameterAsText(1)
replaceText = arcpy.GetParameterAsText(2)
targetFields = arcpy.GetParameterAsText(3)

queryString = '"' + targetFields + '" = ' + "'" + oldText + "'"

with arcpy.da.UpdateCursor(FC, (targetFields,), queryString) as cursor:
    for row in cursor:
        row[0] = replaceText
        cursor.updateRow(row)
3
  • You're almost there.. you do know that your script will replace every row with replaceText. Is it to find a whole string (entire cell contents) or individual words? Commented Mar 11, 2016 at 3:57
  • 1
    Please do not include thanks in your posts. You need to think of the Q&As we do here as being wiki pages. I suspect that you would not expect to see "Thank you ahead!" at the bottom of a page in Wikipedia. Commented Mar 11, 2016 at 4:08
  • @Michael Miles-Stimson Ah I forgot to do an if oldText then replaceText. Commented Mar 11, 2016 at 14:35

1 Answer 1

4

Firstly, let's get the string fields using arcpy.ListFields:

RFields = arcpy.ListFields(FC,"*","String")

which returns a list of fields which may, or may not, work with your update cursor fields, so just to be sure turn them into text:

TextRep = [Fld.name for Fld in RFields ]

This means 'make a list of item name for each item inRFields'. From there use upper() to ensure that comparison is case insensitive then store each row..

Altogether:

import arcpy

FC = arcpy.GetParameterAsText(0)
oldText = arcpy.GetParameterAsText(1)
replaceText = arcpy.GetParameterAsText(2)
targetFields = arcpy.GetParameterAsText(3) # is this really necessary?

queryString = '"' + targetFields + '" = ' + "'" + oldText + "'" # A whole line of text, note the quotes aren't necessary
queryString = targetFields + ' = ' + "'" + oldText + "'" # will work just the same

# just string fields:
RFields = arcpy.ListFields(FC,"*","String")
TextRep = [Fld.name for Fld in RFields ]

# your query string will just confuse the issue, so let's not use it
with arcpy.da.UpdateCursor(FC, TextRep) as cursor:
    for row in cursor:
        rowChanged = False
        for Col in range(len(RFields)):             
            if row[Col].upper() == oldText.upper():
                row[Col] = replaceText
                rowChanged = True # flag to store this row.
        if rowChanged:
            cursor.updateRow(row) # only update changed rows.

From here you can use manipulations like:

if row[Col].find(oldText) >= 0:
    row[Col] = row[Col].replace(oldText,replaceText)

to replace part strings, or do similar things with numbers etc.. it's up to you how far you want to push this framework.

7
  • 1
    How about queryString = " {} = '{}' ".format(targetFields,oldText) ? Commented Mar 11, 2016 at 4:20
  • That's exactly right @Midavalo. I don't think it's needed in this case as it would be double handling to query then test the values and as you need to test each column of each row to find the one that satisfies the query, it would just slow the process down more than skipping unchanged rows (which is fairly quick) except where the table is enormous (millions of records) and the satisfying rows are very few... definitely food for thought. Commented Mar 11, 2016 at 4:24
  • 2
    I just think that's a safer way to concatenate. Using a plus symbol has risks as well as having to juggle extra quote marks etc. But that said I'm unsure how the queryString will work as-is in this situation. Commented Mar 11, 2016 at 4:26
  • Wow, that's quite a cursor @MichaelMiles-Stimson ...don't suppose you could break it down for a python noob a bit more? I, for, example, am not familiar with the [col]. And also, why wouldn't the targetFields parameter be necessary? What I am trying to do is have a parameter that lets you set out what field you want the script to look through, be it one or more fields. Commented Mar 11, 2016 at 14:48
  • 1
    TextRep is the fields parameter, it will contain only the text fields. With a cursor each specified field is treated like a list so ['field1','field2'] field1 is row[0] and field2 is row[1].. perhaps Col is a bad/misleading variable name but Col (x axis) is associated with row(y axis) so why not. If you don't supply a fields list the default is 'all fields' which isn't what you want, you only want the fields that match the type. Try using the python window in ArcCatalog to run potions and print the results to the console to see what the code is doing - that might help explain why. Commented Mar 13, 2016 at 21:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.