In ArcPro 3.2.1, I'm trying to use the Calculate Field Tool within Model Builder to update the values of a field that has several different values. For example, the field currently contains the values of AVE, BLVD, CIR, CT, HWY etc. I want to update these values to the fully spelled out versions: AVENUE, BOULEVARD, CIRCLE, COURT etc. Is there a more efficient way to do this than selecting and calculating each value individually? This will be only one piece of a larger model and I don't want to have 30 select/calculates in the model -- it will be a mess! Thanks in advance.
There's definitely a more elegant way to do this, but I'd do like:
expandStreets(!FieldA!)
def expandStreets(field):
field = field.replace(" AVE", "AVENUE")
field = field.replace(" CT", " COURT")
field = field.replace(" ST", " STREET")
# Etc...
return fieldNote that I have a space in front of the abbreviation so you don't get Streetephen Boulevard
You can do this with a dictionary lookup that the Calculate Field script checks & returns. The code below is for Python, but the same thing could also be accomplished in Arcade, too.
First, you'll need to write a function in the Code Block that does the lookup:
suffixes = {'AVE': 'AVENUE',
'BLVD': 'BOULEVARD',
'CIR': 'CIRCLE',
'CT': 'COURT',
'HWY': 'HIGHWAY',
}
def Suffix(oldValue):
# This will replace any of the values you were prepared for with the new
# values
if oldValue in suffixes:
return suffixes[oldValue]
# And this will keep your original value unchanged, in case you didn't
# prepare for one, otherwise the calculation will fail when it returns a
# value it doesn't recognize.
else:
return oldValue
Then, in the Expression line, you just call that function:
Suffix(!FieldName!)
You still have to write out all 30 known options in that dictionary (Lines 1–6, in the example), but at least it's all contained in the one Calculate Field call.
Yeah this one of the more elegant ways lol
It's also worth noting that your solution presumes suffixes in the same field as street names, and mine presumes suffixes in a standalone field.
In the road data I've worked with in the past, yours is probably the safer assumption, but mine was based on my reading of the OP.
@BethAllen , are your suffixes already in a standalone field, or do you have to extract them from a full street address (e.g., getting "ST" from "1521 STEPHEN ST")? Because that could significantly change how best to approach this.
Thanks to all for the responses.
My suffixes are already in a standalone field. Actually all of the components of the street name are already parsed into different fields but each of the components (other than the core street name) are abbreviated. I need to spell them out fully for each field.
In that case, the script I shared should work for your data. Let me know how it goes!
Because I wanted the challenge and for anyone who lands on this page in the future looking for a solution where the suffixes are in the same field as the street name, an easy-ish fix is to just modify the code to break it out into parts based on where the spaces are:
suffixes = {'AVE': 'AVENUE',
'BLVD': 'BOULEVARD',
'CIR': 'CIRCLE',
'CT': 'COURT',
'HWY': 'HIGHWAY',
}
def Suffix(oldValue):
# First, we need to deal with NULL, because it will break the next bit
# Let's just pass them on through.
if oldValue is None:
return oldValue
# Next, let's break up that street address. For simplicity, this assumes a
# space between each part. You'll probably get some extraneous splitting
# this way with multiword street names (e.g., "1210 St Stephen St")
streetParts = oldValue.split(' ')
# Now, we just need to look at the last component
if streetParts[-1] in suffixes:
streetParts[-1] = suffixes[streetParts[-1]]
# I've seen some cases where an address like 1210A would put the A at the
# end. I hate them, but I've encountered them. If you have some, add
# these lines to catch those cases, too.
if streetParts[-2] in suffixes:
streetParts[-2] = suffixes[streetParts[-2]]
# Lastly, we merge everything back together. Unknown suffixes end up
# unchanged automatically, so no need for the else statement like last time
# We join them with a single space, since that's what we split against last
# time.
return ' '.join(streetParts)Essentially, this is what happens:
"1210 ST STEPHEN ST" becomes ["1210", "ST", "STEPHEN", "ST"]. Then, I look at the last two elements, which are "St" and then "Stephen". One of those matches my dictionary, so it changes it, leaving us with ["1210", "ST", "STEPHEN", "STREET"], and then we just merge it all back together.
The code above isn't foolproof, of course. "1210 ST STEPHEN ST A 10" (1210 St Stephen St, Unit A10) would fail to be corrected. Combined street addresses get messy fast, so there's only so much you can do. Much better to split the fields if you can and do it the way I suggested above for BethAllen.
Thank you for your thoughtful responses! So I decided to try your recommendation on another field first (one with fewer and simpler values) since I know nothing about coding! I'm using a street pre-directional field called "AVDIR". The only four values are N, S, E, W which I need fully spelled out. Below is what used; however when I run it fails (see third screenshot below).
Two things:
def compass(field):
dirDict = {"N":"North"}
#code
#code
x = dirDict[field]
returnTry changing the name and the location of the dictionary and see if that fixes it.
TL;DR: Line 1 and Line 7 should have different names, and Line 8 should be looking at the name from Line 1.
The issue is that you used the same name for both the function and the dictionary, and line 1 is looking at the wrong one. This one is partly on me. Python is case-sensitive, and since I tend to use Title Case for Functions and lower case for variables, I didn't notice that I'd called both a variation of "Suffix" in my examples, which could potentially be confusing.
Since Python is case-sensitive, you could just capitalize one of them like this, and it would work (note the lowercase on line 1 and line 4, compared to the uppercase on line 3):
compass = {'N': 'NORTH', ...}
def Compass(oldValue):
if oldValue in compass
But it's probably better to get in the habit of being more explicitly different, for safety & clarity:
compassDict = {'N': 'North', ...}
def CompassFunc(oldValue):
if oldValue in compassDict: