0

I'm trying to update a column based on the value in the next row, but cursor.next() is giving unexpected behavior. When I identify the next_row using cursor.next(), it is resetting my row in cursor! The result is my loop goes over every other row, not every row. What am I missing? (Code below is just to show cursor behavior; code for row update for column has been stripped.)

Code

import arcpy
temp1 = r'mypath\mypointfile.shp'
with arcpy.da.UpdateCursor(temp1, ['OID@']) as cursor:
    for row in cursor:
        try:
            row_next = cursor.next()
            print 'current_oid=', row[0], 'next_oid=', row_next[0]
        except StopIteration:
            print 'no rows'
        except:
            print 'something else went wrong'

Actual Prints

current_oid= 0 next_oid= 1
current_oid= 2 next_oid= 3
current_oid= 4 next_oid= 5
current_oid= 6 next_oid= 7
current_oid= 8 next_oid= 9

Expected Prints

current_oid= 0 next_oid= 1
current_oid= 1 next_oid= 2
current_oid= 2 next_oid= 3
current_oid= 3 next_oid= 4
current_oid= 4 next_oid= 5

UPDATE

Purpose of this is to only keep rows that are the first or last point in the group.

Example attributes:

oid group   id
0   1   1
1   1   2
2   1   3
3   2   1
4   2   2
5   3   1
6   3   2
7   3   3
8   3   4
9   3   5

Desired attributes:

group   id
1   1
1   3
2   1
2   2
3   1
3   5

Original code, that didn't work because cursor.next() skips rows:

with arcpy.da.UpdateCursor(temp1, ['OID@', 'group', 'id']) as cursor:
    for row in cursor:
        group_current = row[1]
        id_current = row[2]
        if id_current == 1:
            check = "keep"
        else:
            try:
                row_next = cursor.next()
                group_next = row_next[1]
                id_next = row_next[2]
                if id_next == 1:
                    check = "keep"
                else:
                    check = "remove"
            except StopIteration:
                check = "keep"
            except:
                print 'something else went wrong'
            if check != "keep":
                cursor.deleteRow()
3
  • 2
    next isn't needed, because you're in a for loop. You can't read ahead, but you can preserve the current in previous then act on that later.
    – Vince
    Commented Mar 28, 2019 at 3:14
  • @Vince As stated in question, purpose is to perform UpdateCursor on a specific column based on values in another column. The printing of the oid values was for illustration of skipping behavior. I will be calculating the value in "mycol" in row4 based on the value in "myotherCol" in row5.
    – a11
    Commented Mar 28, 2019 at 3:25
  • 1
    No, you can't read ahead without losing the current row. You need to use cursor as it actually operates, not how you wish it did. What you can do is save your values in a dictionary with a SearchCursor scan with an OID key, the run an update pass later. I've cached tens of millions of rows with a 32-bit Python and hundreds of millions with 64-bit Python, so 15k rows is a drop in the bucket.
    – Vince
    Commented Mar 28, 2019 at 4:08

2 Answers 2

0

Yes, this is expected as it's how python generators work, once a value is consumed, it's gone. If your feature class isn't toooo big, pull all the rows into a list, then use enumerate and indexing to access the next row. E.g.

cursor = ((i,) for i in range(10))  # Fake a cursor as I'm not at a PC with ArcGIS
rows = list(cursor)  # Exhaust the generator
for i, row in enumerate(rows):
    try:
        row_next = rows[i+1]
        print(row[0],row_next[0])
    except IndexError:
        print('no rows')

Output:

0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
no rows

Or... turn it around, grab the first row before the loop then refer to the previous row instead of the next row:

prev_row = next(cursor)
for row in cursor:
    print(prev_row[0], row[0])
    prev_row = row

Output:

0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
3
  • I'm doing this for many files, and my smallest one has about 15k rows. As stated in question, purpose is to perform UpdateCursor on a specific column based on values in another column. The printing of the oid values was for illustration of skipping behavior.
    – a11
    Commented Mar 28, 2019 at 3:23
  • Either method should work. Use a SearchCursor to get the list before using the UpdateCursor, a few million values in a list isn't going to be an issue (unless you pull in geometry as well).
    – user2856
    Commented Mar 28, 2019 at 3:28
  • answer accepted because it addresses cursor behavior in original question; in case anyone has a similar issue, I posted working code below as another answer
    – a11
    Commented Mar 28, 2019 at 4:35
0

Thanks to Vince's comments and user2856's answer/comments, the following worked. Elapsed time = 30.17 seconds for 52,000 row, which seems slow.

temp1 = r'mypath\mypointfile.shp'
import datetime
now = datetime.datetime.now()

cursor = arcpy.da.SearchCursor(temp1, ['OID@', 'group', 'id'])
rows = list(cursor)
dict_ids = {}
for i, row in enumerate(rows):
    oid = row[0]
    group_current = row[1]
    id_current = row[2]
    if id_current == 1:
        check = "keep"
    else:
        try:
            row_next = rows[i + 1]
            id_next = row_next[2]
            if vid_next == 1:
                check = "keep"
            else:
                check = "remove"
        except IndexError:
            check = "keep"
        except:
            print 'something else went wrong'
    dict_ids.update([(oid, check)])
del i, row, check
with arcpy.da.UpdateCursor(temp1, ['OID@']) as cursor:
    for row in cursor:
        oid = row[0]
        if oid in dict_ids:
            check = dict_ids.get(oid)
        else:
            print 'error with dictionary'
            sys.exit()
        if check != "keep":
            cursor.deleteRow()

later = datetime.datetime.now()
elapsed = later - now
print elapsed
2
  • That is slow, your code takes 2 seconds for me on a local FGDB feature class with 52,000 records. Make sure you're not running it on data on a network drive.
    – user2856
    Commented Mar 29, 2019 at 3:04
  • yes, it is slow. no network, all local.
    – a11
    Commented Mar 29, 2019 at 3:32

Your Answer

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

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