how to check if cursor object is empty?

35614
20
02-02-2012 06:15 AM
kyleturner
New Contributor III
It seems an easy task, but I can't figure out how to check if the searchcursor object is empty.
I know that it is empty because the query fails....i.e. arcpy.searchcursor(FC, query).
However, even if it (arcpy.searchcursor..) fails, it still returns a geoprocessing cursor object...

rows = gp.SearchCursor(crosswalk_dbf,query)

print rows ---> returns
<geoprocessing cursor object object at 0x029C7CC0>


I can't check if it's empty, because, well, it's not.
And for reasons I obviously don't understand.......

rows = gp.SearchCursor(crosswalk_dbf,query)
for row in rows:
  if not row.isNull(fieldname)

------------

is always 'true'.

Please help.

I'm using Arc10 SP3, win 7 64bit.

Many thanks.
Tags (2)
0 Kudos
20 Replies
kyleturner
New Contributor III
Thanks for your help gentlemen.

Chris, I tried your getcount_management option, but it seems that you can only use it on a layer/fc. I tried count = int(gp.getcount_management(rows).getoutput(0))......which fails.
I suppose I could have made a layer, but I figured out another way.

Here's how I fixed it:
rows = gp.SearchCursor(fc,query)
row = rows.next()
if not (row == None):
   do what you need to do

elif (row == None):
  well, then someone needs to fix the dataset.


Thanks again gentlemen for your time.
Cheers.
0 Kudos
GeorgeNewbury
Occasional Contributor
Be careful when you do the 'row = rows.next()' because then you are moving the cursor forward, which could throw off any subsequent looping.

I prefer the method Matthew described:

    row, rows= None, None
    rows= arcpy.SearchCursor(inputFC)
    for row in rows:
        -- do work---
            
    del row, rows


Of course you can put your query clause in there to or do any sorting. If you wanted to know how many records were in there you could add a count:

    rowCount = 0
    row, rows= None, None
    rows= arcpy.SearchCursor(inputFC)
    for row in rows:
        rowCount += 1
        -- do work---
            
    del row, rows


If the count is 0 then rows was empty. It used be to be that you would use the '.next()' method for a while loop, but now with the new syntax ('row in rows') I try to avoid the next method.

-George
0 Kudos
PhilMorefield
Occasional Contributor III
Here's another way...

import types
import arcpy as ap

rows = ap.SearchCursor(myTable, sql_statement)
row = rows.next()

if type(row) != types.NoneType:
    # do a bunch of work
0 Kudos
ChrisSnyder
Regular Contributor III
An even shorter way:

if arcpy.SearchCursor(myTable, sql_statement).next() == None:
   print "Empty cursor!"


The only trick though is to figure out if your SQL is returning any records or not... Which could be accomlished through a cursor statment like the one above or a combo of the MakeFeatureLayer/MakeTableView and the GetCount tool. Which one is faster? I dunno... It would also improve performance if the fields in your SQL were properly indexed also.
0 Kudos
BlakeTerhune
MVP Regular Contributor

I know I'm playing necromancer and raising this discussion from the dead but I was looking for an answer to this question and found my own solution that I thought I would post. Maybe someone else has come up with something better by now.

Thanks to Joshua Bixby‌ for his blog post on cursors, here's what I came up with:

with arcpy.da.SearchCursor(in_table, "*") as s_cursor:
    if sum(1 for row in s_cursor):  ## Counts items in cursor, returns 0 if empty
        s_cursor.reset()  ## Return cursor back to the first row after counting
        for row in s_cursor:
            # Process rows
            print(row)
    else:
        print("Cursor is empty!")‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The check for rows will iterate through the cursor leaving nothing for the .next() item so you have to .reset() the cursor to start from the beginning again. In my case, I couldn't just count the rows with GetCount_management() in the original feature class because I was using a where clause in the search cursor.

0 Kudos
XanderBakker
Esri Esteemed Contributor

To check if a applying a where clause returns no results can also be done with the GetCount tool. Just make a featurelayer and check the result. Below some examples including using the search cursor on empty results, which really is no problem at all.

def main():
    import arcpy
    fc1 = r'D:\Xander\GeoNet\Ellipse\data.gdb\Ellipses'
    fc2 = r'D:\Xander\GeoNet\Ellipse\data.gdb\Ellipses_empty'

    # get count on a featureclass
    cnt1 = getCount(fc1)
    cnt2 = getCount(fc2)

    print "fc1 has {0} features".format(cnt1)
    print "fc2 has {0} features".format(cnt2)

    # Cursor on a featureclass with features
    max_oid = None
    with arcpy.da.SearchCursor(fc1, ('OID@')) as curs:
        for row in curs:
            oid = row[0]
            if max_oid is None:
                max_oid = oid
            elif oid > max_oid:
                max_oid = oid
    print "max_oid for fc1:", max_oid

    # Cursor on a featureclass without features
    max_oid = None
    with arcpy.da.SearchCursor(fc2, ('OID@')) as curs:
        for row in curs:
            oid = row[0]
            if max_oid is None:
                max_oid = oid
            elif oid > max_oid:
                max_oid = oid
    print "max_oid for fc2:", max_oid


    # get count on a feature layer (with where clause)
    where = "Ellipse = 1"
    fl1 = arcpy.MakeFeatureLayer_management(fc1, "flay1", where)
    fl2 = arcpy.MakeFeatureLayer_management(fc2, "flay2", where)
    cnt1 = getCount(fl1)
    cnt2 = getCount(fl2)

    print "fl1 has {0} features".format(cnt1)
    print "fl2 has {0} features".format(cnt2)


   # Cursor on a featurelayer (where clause) with features
    max_oid = None
    with arcpy.da.SearchCursor(fl1, ('OID@')) as curs:
        for row in curs:
            oid = row[0]
            if max_oid is None:
                max_oid = oid
            elif oid > max_oid:
                max_oid = oid
    print "max_oid for fl1:", max_oid


    # Cursor on a featureclass (where clause) with features, but without results
    where = "Ellipse = -1" # there are no features witch match this where clause
    max_oid = None
    with arcpy.da.SearchCursor(fc1, ('OID@'), where) as curs:
        for row in curs:
            oid = row[0]
            if max_oid is None:
                max_oid = oid
            elif oid > max_oid:
                max_oid = oid
    print "max_oid for fc1 (with where clause, no match):", max_oid


def getCount(fc):
    return int(arcpy.GetCount_management(fc).getOutput(0))

if __name__ == '__main__':
    main()

This will print:

fc1 has 722 features
fc2 has 0 features
max_oid for fc1: 722
max_oid for fc2: None
fl1 has 361 features
fl2 has 0 features
max_oid for fl1: 361
max_oid for fc1 (with where clause, no match): None
JoshuaBixby
MVP Esteemed Contributor

As Xander Bakker‌ points out, the geoprocessing tool approach to addressing your issue involves creating a feature layer and counting that, and then passing the feature layer to the search cursor if there are results.

Looking at it from purely a Python perspective, i.e., not involving geoprocessing tools, the major issue with the code provided is that it completely consumes the iterable to determine if there is just one or more records.  There are several ways in Python to check if an iterable is empty and then do one thing or another depending on the results.

One approach that doesn't fully consume the iterable to check for the existence of items uses next() with a sentinel value:

s_cursor = arcpy.da.SearchCursor(in_table, "*")
with s_cursor:
    if next(s_cursor, None) is not None:
        s_cursor.reset()
        for row in s_cursor:
            print row
    else:
        print "Cursor is empty!"

Another approach sets the variable for holding what is returned by the iterable to a sentinel value and then checks after processing the iterable to see if anything was processed:

row = None
with s_cursor:
    for row in s_cursor:
        print row
    if row is None:
        print "Cursor is empty!"
BruceHarold
Esri Regular Contributor

Another one is wrapping the cursor in a generator function and seeing if StopIteration is raised when you request a row.

JoshuaBixby
MVP Esteemed Contributor

Since the cursor itself will raise a StopIteration error if there isn't a row to request, I guess I don't understand the value of wrapping it in a generator function to raise a StopIteration error.

0 Kudos
BruceHarold
Esri Regular Contributor

Using a generator function is  a pattern I find useful so I thought I would mention it.

Handling StopIteration lets you automate returning something you design, like below I return a dictionary if the cursor is empty or not.

# Define row generator
def rowGenerator(table,fields,sortField=None):
    if sortField is not None:
        sql = [None,'ORDER BY ' + sortField]
        cursor = arcpy.da.SearchCursor(table,fields,sql_clause=sql)
    else:
        cursor = arcpy.da.SearchCursor(table,fields)
    with cursor:
        for row in cursor:
            yield row

# Create row generator
if postalField:
    rowGen = rowGenerator(inTable,cursorFields,postalField)
else:
    rowGen = rowGenerator(inTable,cursorFields)

# Define recordset builder
def recordsetBuilder(rowgen):
    records = []
    for i in range(0,1000):
        try:
            data = next(rowgen)
            attributes = {'attributes':{recordFields:data
                                        for i in range(len(data))}}
            records.append(attributes)
        except StopIteration:
            break
    return {'records':records}