2

I have two tables (actually both are feature classes in filegeodatabase). They pretty much identical except. one is in lat/lon. second one is projected. both have field line_length and objectid. Second one has values in field line_length. how do I copy these values (based on objectid field match) from second table into the fist one.

The snippet of my code:

Part first. Create dictionary from second dataset. Copy values from second dataset into dictionary. value that I will be using for matching is goint to be key, the value I need to copy to my first dataset are going to be values. This part works ok.

my_dict = dict()
fc = r'H:\NetworkAnalyst\TopologyVer4\firetrials.gdb\ft_mb_afterIteration_lambert'
fields = ['OBJECTID', 'line_length']
with arcpy.da.SearchCursor(fc, fields) as cursor:
    for row in cursor:
        a_id = row[0]
        b_len = row[1]
        my_dict[a_id] = b_len

print "dictionary done"

Second part. Update field "line_length" in the first dataset with values from second dataset, which are now in dictionary. Match records by OBJECTID field.

rows = arcpy.UpdateCursor(r'H:\NetworkAnalyst\TopologyVer4\firetrials.gdb\ft_mb_afterIteration_lambert')
for key, value in my_dict:
    match_value = key
    update_value = value
    for row in rows:
        print match_value, update_value
        if row.OBJECTID == match_value:
        rows.setValue("line_lenght", update_value)

I am getting an error for line for key, value in my_dict:

Error is " TypeError: 'int' object is not iterable "

Any ideas?

6
  • I'm not sure what's causing your error (what line is being reported as the origin for the error?) but it looks like the first line in your second snippet should actually be inside the "for key, value in my_dict" iteration. The reason being is that it will reach the end of the cursor after the very first iteration of the dictionary and will need to be recreated for the next iteration. Someone correct me if I'm wrong.
    – nwduncan
    Mar 1, 2016 at 5:00
  • Using a dictionary is not the most efficient solution to this problem. A nested update DA cursor within a DA query cursor is the canonical solution.
    – Vince
    Mar 1, 2016 at 5:00
  • Vince, could you explain me how to do it?
    – lida
    Mar 1, 2016 at 5:06
  • nwduncan, do you mean to swap places of my first and second lines in the second snippet?
    – lida
    Mar 1, 2016 at 5:08
  • Yes. I do agree with @Vince though, this is not a terribly good way of doing it - You'll be iterating over every line in your update cursor for every key/value in your dictionary in the hopes that you get a match.
    – nwduncan
    Mar 1, 2016 at 5:10

2 Answers 2

6

Pre-code:

import arcpy

fc_src = r"C:\ArcTutor\BuildingaGeodatabase\Montgomery.gdb\Landbase\Blocks"
fc_dest = r"C:\ArcTutor\BuildingaGeodatabase\Montgomery.gdb\Landbase\Blocks_Geo"

What Vince was referring to as nested cursors:

Code:

with arcpy.da.SearchCursor(fc_src,["OBJECTID","Acres"]) as search_cur:
    for search_row in search_cur:
        with arcpy.da.UpdateCursor(fc_dest,["OBJECTID","AcresObtained"]) as upd_cur:
            for upd_row in upd_cur:
                if upd_row[0] == search_cur[0]:
                    upd_row[1] = search_row[1]
                    upd_cur.updateRow(upd_row)

A dictionary based solution (I use this most often):

search_feats = {f[0]:f[1] for f in arcpy.da.SearchCursor(fc_src,["OBJECTID","Acres"])}

with arcpy.da.UpdateCursor(fc_dest,["OBJECTID","AcresObtained"]) as upd_cur:
    for upd_row in upd_cur:
        upd_row[1] = search_feats[upd_row[0]]
        upd_cur.updateRow(upd_row)

Additional comments:

  • always use the da cursors (arcpy.da.UpdateCursor, not arcpy.UpdateCursor)
  • use the list/dictionary comprehensions, particularly when iterating arcpy cursors

Update: explaining some of the concepts in the dictionary alternative.

f[0]:f[1]

means we are taking the first value in the tuple (cursor returns a list of two-values tuple, such as [(12,45.678),(13,78.654)], which represents the OBJECTID, and make it a key of the dictionary; we map this key into a value of Acres (second element in the tuple).

This is the same as writing:

search_feats = {}
for f in arcpy.da.SearchCursor(fc_src,["OBJECTID","Acres"]):
    search_feats[f[0]] = f[1]

The dictionary comprehensions are just faster and less code can be written and then read.

Another thing.

upd_row[1] = search_feats[upd_row[0]]

Here we assign the AcresObtained field (which is the second value in the tuple in the update cursor) to be a value that is found for this OBJECTID key in the dictionary search_feats.

Could be written as

row_update_objectid = upd_row[0]
upd_row[1] = search_feats[row_update_objectid]
6
  • Hi Alex, thanks for your reply. I have just tested both variation. I am getting an error. Here is example of the error when I run the variation with dictionary: File "H:\NetworkAnalyst\RemoveShortLines.py", line 58, in <module> search_feats = {f[0]:f[1] for f in arcpy.da.SearchCursor(fc_src,["OBJECTID", "line_lenght"])} File "H:\NetworkAnalyst\RemoveShortLines.py", line 58, in <dictcomp> search_feats = {f[0]:f[1] for f in arcpy.da.SearchCursor(fc_src,["OBJECTID", "line_lenght"])} RuntimeError: A column was specified that does not exist.
    – lida
    Mar 1, 2016 at 11:21
  • I thought that it could be wrong column "line_lenght" (the only part I changed from your example), but this column exist in both files and is called the same name. Is there anything else, you think, that can cause this error? thanks!
    – lida
    Mar 1, 2016 at 11:26
  • Check OBJECTID field name - alias might hide the actual name. Are you sure it's "line_lenght", maybe "line_length"? Mar 1, 2016 at 15:52
  • Hi Alex. Yes, you were right. There was a typo in field name. Now it works perfect. Thank you. Two more questions though. The line from your second code: search_feats = {f[0]:f[1] for f in arcpy.da.SearchCursor(fc_src,["OBJECTID", "line_length"])} What is f[0]:f[1] ? And this line is not clear either upd_row[1] = search_feats[upd_row[0]] ?
    – lida
    Mar 1, 2016 at 20:22
  • I updated my answer. I suggest you looking at some basics of Python to catch up, there are great resources available gis.stackexchange.com/questions/53816/… Mar 2, 2016 at 6:10
0

I'm unable to test this currently, but try the following as a replacement for your second snippet:

layer = r'H:\NetworkAnalyst\TopologyVer4\firetrials.gdb\ft_mb_afterIteration_lambert'
fields = ["OBJECTID@", "line_length"]
with arcpy.da.UpdateCursor(layer, fields) as cursor:
    for row in cursor:
        if row[0] in my_dict:
            row[1] = my_dict[row[0]]
            cursor.updateRow(row)
        else: pass

This will go over every row in your update cursor, and if the current row's objectID is in the dictionary it will update the field 'line_length' with the value matching that key/object ID in the dictionary.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.