Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save alexfriant/c553d9a8e81e0c99a42d to your computer and use it in GitHub Desktop.
Save alexfriant/c553d9a8e81e0c99a42d to your computer and use it in GitHub Desktop.
Provides a way to permanently attach a field to another table, as in a one to one join, but without performing a join then exporting a new dataset. Operates in place by creating a new field on the existing dataset.
import arcpy
def permanent_join(target_table, target_attribute, source_table, source_attribute, attribute_to_attach, rename_attribute=None):
"""
Attaches a field to a dataset in place in ArcGIS - instead of the alternative of doing an actual join and then saving out a new dataset
Only works as a one to one join.
:param target_table: the table to attach the joined attribute to
:param target_attribute: the attribute in the table to base the join on
:param source_table: the table containing the attribute to join
:param source_attribute: the attribute in table2 to base the join on
:param attribute_to_attach: the attribute to attach to table 1
:param rename_attribute: string to indicate what to rename the field as when it's joined.
:return: None
"""
# first, we need to find the information about the field that we're attaching
join_table_fields = arcpy.ListFields(source_table)
for field in join_table_fields:
if field.name == attribute_to_attach: # we found our attribute
base_field = field
break
else:
raise ValueError("Couldn't find field to base join on in source table")
type_mapping = {"Integer": "LONG", "OID": "LONG", "SmallInteger": "SHORT", "String": "TEXT"} # ArcGIS annoyingly doesn't report out the same data types as you need to provide, so this allows us to map one to the other
if base_field.type in type_mapping.keys(): # if it's a type that needs conversion
new_type = type_mapping[base_field.type] # look it up and save it
else:
new_type = base_field.type.upper() # otherwise, just grab the exact type as specified
if rename_attribute:
new_name = rename_attribute
else:
new_name = base_field.name
# copy the field over other than those first two attributes
arcpy.AddField_management(target_table, new_name, new_type, field.precision, field.scale, field.length, field_alias=None, field_is_nullable="NULLABLE", field_is_required=field.required, field_domain=field.domain)
arcpy.AddMessage("New field '{0}' added to {1}".format(new_name,target_table))
join_data = read_field_to_dict(source_table, attribute_to_attach, source_attribute) # look up these values so we can easily just use one cursor at a time - first use the search cursor, then the update cursor on the new table
updater = arcpy.UpdateCursor(target_table)
for row in updater:
if row.getValue(target_attribute) in join_data.keys(): # since we might not always have a match, we need to check, this should speed things up too
row.setValue(new_name, join_data[row.getValue(target_attribute)]) # set the value for the new field to the other table's value for that same field, indexed by key
updater.updateRow(row)
del updater
arcpy.AddMessage("\nYour table '{0}' now has the '{1}' field from the '{2}' table appended as the field called '{3}'.\n\n".format(arcpy.Describe(target_table).name, attribute_to_attach, arcpy.Describe(source_table).name, new_name))
arcpy.AddMessage("REMEMBER TO REFRESH YOUR TABLE TO SEE THE RESULTS!!\n\n")
def read_field_to_dict(input_table, data_field, key_field):
"""
Given an arcgis table and a field containing keys and values, reads that field into a dict based on the key field
:param table: an ArcGIS table (or feature class, etc)
:param data_field: the field that contains the data of interest - these values will be the dictionary values
:param key_field: the field that contains the keys/pkey values - these values will be the keys in the dictionary
:return: dict of the data loaded from the table
"""
data_dict = {}
rows = arcpy.SearchCursor(input_table)
for row in rows:
data_dict[row.getValue(key_field)] = row.getValue(data_field)
del rows
return data_dict
#########################################################################################
#
# Requirements: You'll need ArcGIS Desktop 10.1 or higher with Python 2.7+
#
# Make sure you have the "ArcGISPermanentOneToOneJoin.py" file in the same directory
# as this script.
#
# This script is designed to run as an ArcToolbox script, so add this script to an
# ArcToolbox of your choice, and make sure to add the required parameters for that
# script reference.
#
#########################################################################################
import arcpy
from ArcGISPermanentOneToOneJoin import permanent_join, read_field_to_dict
##ArcToolbox Script Input Parameters (suggested script parameter names are in the comments)
#The target table you want to join a field to:
#For Data_Type, choose "Feature Class"
target_table = arcpy.GetParameterAsText(0)
#The field name in your target table to base the join on:
#For Parameter_Properties.Obtained_From, choose the target table parameter
target_attribute = arcpy.GetParameterAsText(1)
#The source table containing the field you want to join to the target table:
#For Data_Type, choose "Feature Class"
source_table = arcpy.GetParameterAsText(2)
#The field name in your source table to base the join on:
#For Parameter_Properties.Obtained_From, choose the source table parameter
source_attribute = arcpy.GetParameterAsText(3)
#The field you want to copy over to the target table:
#For Parameter_Properties.Obtained_From, choose the source table parameter
attribute_to_attach = arcpy.GetParameterAsText(4)
#If you need to rename the field, give it a new name here:
#For Parameter_Properties.Type, choose "Optional"
rename_attribute = arcpy.GetParameterAsText(5)
arcpy.AddMessage("Parameters gathered.")
##Attempt to execute the join function
try:
permanent_join(target_table, target_attribute, source_table, source_attribute, attribute_to_attach, rename_attribute)
except NameError as e:
arcpy.AddMessage("There was a Name Error: {0}".format(e))
raise
except:
arcpy.AddMessage("There was an error: {0}".format(sys.exc_info()[0]))
raise
@alexfriant
Copy link
Author

ArcGIS Pro now offers this functionality out of the box with their Data Engineering tools > Integrate > Join

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment