Select to view content in your preferred language

How do I delete specific fields in multiple feature classes (not necessarily the same fields)?

1665
10
05-08-2023 01:12 PM
bcarpenter87
New Contributor II

I was given this script to modify it to specify certain fields to delete within certain feature classes. How it is now, all fields I put in the list will be deleted in the specified geodatabase. What would I include in this script to further specify that certain fields within a feature class are deleted while certain other fields in another feature class are deleted, etc.? I am a very beginner programmer and intern and my boss gave me this to experiment with and work on, suggesting geonet/ESRI community as a good resource. I've included the script.

Thank you for any help,

Ben

0 Kudos
10 Replies
DavidPike
MVP Frequent Contributor
import arcpy
from arcpy import env
env.workspace = r"Z:\ArcGIS\Projects\Other.gdb"
p = arcpy.mp.ArcGISProject('current')
m = p.listMaps()[0]
fcList = arcpy.ListFeatureClasses()
for fc in fcList:
	dropFields = ['add new filedname here', 'aandefirm','accountid','ACTIVEFLAG','andefirm','appended','As_BuiltDa','ASBUILTDATE','AsBuiltDwg']
	arcpy.DeleteField_management(fc, dropFields)
print (fc + ' Dropped standard fields!')

What is the exact logic of how you want it to drop specific fields for specific features?

0 Kudos
bcarpenter87
New Contributor II

I suppose I haven't put too much thought into it. Here is another text file of all of the feature classes on the left side with their particular fields to be deleted on the right side. I've located where all the feature classes are and am going to proceed to import them into the Other.gdb so that the Python script can operate on them in there for testing.

0 Kudos
DavidPike
MVP Frequent Contributor

Ah OK, should be pretty simple.  Do you have the feature class/table names rather than their aliases?  Would make it simpler.

0 Kudos
bcarpenter87
New Contributor II

Let me see....

0 Kudos
bcarpenter87
New Contributor II

Traffic_Geonet_eg.PNG

Like what is seen in the gdb?

0 Kudos
BlakeTerhune
MVP Regular Contributor

@bcarpenter87 wrote:

Like what is seen in the gdb?


Yes, that is what @DavidPike was getting at regarding the "feature class/table names rather than their aliases." You would need to update that text file, replacing the alias name you have with the actual name in the gdb. If that's not reasonable and those are infact the actual alias names, I suppose you can still do it, there'd just be a few extra steps.

Either way, how I would do it is create a dictionary where the key is the gdb fc name and value is a list (or better yet a set, in case of duplicates) of field names that need to be deleted for that feature class. Then loop through your dictionary items and DeleteField one by one. It'll probably take a while with that many fields. Also keep in mind that you won't be able to delete system generated fields like ObjectID and GlobalID so prepare to handle that.

0 Kudos
bcarpenter87
New Contributor II

I just need guidance for how to proceed with an optimal way of deleting only the fields tied to the features in the list without deleting all fields in the list for all features. And some of the features have the same fields in them.

0 Kudos
by Anonymous User
Not applicable

I took @BlakeTerhune's method and converted the list to a dictionary by using a little regex to split on the varying multiple spaces, filtering out the None's (Air Vacuum Valve has one).  You can add filters to the dictionary creation to skip the fields such as globalid or objectid if they cannot be deleted and is causing the DeleteFields to fail:

 

# Create the dictionary of fields from the txt file by splitting the spaces.
with open(r'path to\specificdeletefields.txt', 'r') as txt:
    for line in txt:
        # split on two or more spaces
        # \s <- any whitespace
        # {2,} <- cnt to match
        splt = re.compile('\s{2,}')
        vals = splt.split(line)
        try:
            if vals[1]: # don't append the None's to the list
                if not fcFieldDict.get(vals[0]):
                    fcFieldDict[vals[0]] = [vals[1].strip()]
                else:
                    if vals[1] not in fcFieldDict[vals[0]]: #avoid appending duplicates
                        fcFieldDict[vals[0]].append(vals[1].strip())

        except:
            print(vals)

 

Then you have two options- you can delete the fields in the existing database by iterating over the toc lyr names using the listLayers(), getting their path to the underlying dataset from the Describe path, and getting the fields from the dictionary from a lookup:

 

lyrList = m.listLayers()

for fc in lyrList:
    fcDesc = arcpy.Describe(fc)
    fieldsToDrop = fcFieldDict.get(fcDesc.nameString)
    if fieldsToDrop:
        fcPath = fcDesc.path
        arcpy.DeleteField_management(fcPath, fieldsToDrop)
        print(f'{fc} Dropped {fieldsToDrop}!')
    else:
        print(f'Didnt find matching fc for {fc}')

 

or do a field map and save the featureclass to another workspace without the fields you want removed:

 

fcList = m.listLayers()

for fc in fcList:
    fcDesc = arcpy.Describe(fc)
    fieldsToDrop = fcFieldDict.get(fcDesc.nameString)
    if fieldsToDrop:
        
        fcPath = fcDesc.path
    
        fldMap = arcpy.FieldMappings()
        # Creating field maps
        fldMap.addTable(fc.pth)
    
        # Removing unwanted fields
        for field in fldMap.fields:
            if not field.required and field.name in fieldsToDrop:
                fldMap.removeFieldMap(fldMap.findFieldMapIndex(field.name))
    
        arcpy.FeatureClassToFeatureClass_conversion(fcPath, r'path to other.gdb', os.path.basename(fcDesc.name), '', fldMap)
    else:
        print(f'Didnt find matching fc for {fc}')

 

 

bcarpenter87
New Contributor II

Hi Jeff. Thanks for looking at this for me. So I have all of the layers (not all of them will be affected of course, only the ones indicated in the txt) into my 'Experiment.gdb. 

I ran the first part of the code you provided in PyCharm just to see what it did. And I think it did what it was supposed to? I've provided an image.

With things set in place, I just needed a few more dots connected for me. How do I run the first part of the your code with either of the second part of the code to get to where I want to be? 

I see that the layers in the gdb have underscores in them. Will I need to change them as @BlakeTerhune suggested?  Do I perform all of this in the Python Window?

Thanks for your time.

Ben

0 Kudos