Select to view content in your preferred language

Cool cursor dictionary constructor one-liner

3524
9
03-08-2012 12:44 PM
ChrisSnyder
Regular Contributor III
I bet someone else will think this is cool too.

In one line, turn your attribute table into a Python dictionary. Of course faster (and actually one line not 3) if you don't have to use .getValue!

Apparently there is a bit more memory overhead doing it this way, but hey...

keyField = "OBJECTID"
valueField = "DIFFERENT_ID"
exampleDict = dict([(searchRow.getValue(keyField), searchRow.getValue(valueField)) for searchRow in arcpy.SearchCursor(myTable)])


http://www.python.org/dev/peps/pep-0274/
Tags (2)
0 Kudos
9 Replies
ChrisSnyder
Regular Contributor III
or better yet...

exampleDict = dict([(r.KEY, (r.VALUE1, r.VALUE2)) for r in arcpy.SearchCursor(myTbl)])
0 Kudos
MathewCoyle
Frequent Contributor
I like the cut of your jib, I'll have to try implementing this somewhere.
0 Kudos
RDHarles
Occasional Contributor
I like it, Chris!
Useful and simple.
0 Kudos
PhilMorefield
Occasional Contributor III
I have a feeling this is going to save me a ton of time some day. Well done.
0 Kudos
PhilMorefield
Occasional Contributor III
or better yet...

exampleDict = dict([(r.KEY, (r.VALUE1, r.VALUE2)) for r in arcpy.SearchCursor(myTbl)])

I've always liked this little piece of code, but a couple of things have always bothered me. The dictionary that results gives you the attributes of your 'KEY' as a series of values (r.VALUE1, r.VALUE2), but you lose the attribute name. Working with big datasets with many 'VALUE's could be difficult. Also, it requires that you go in and enter each field name you wanted included in the output dictionary. Again, with big datasets this may be problem.

This little ditty uses nested list comprehension to solve those problems:
# for this example, use the Census 2000 state shapefile
myShp = 'R:\\data\\Census\\2000\\Shape\\ST.shp'

myDict = dict([((r.ST_ABBREV, f.name), r.getValue(f.name)) for f in ap.ListFields(myShp) for r in ap.SearchCursor(myShp)])

>>> myDict[('WY', 'STATE_NAME')]
>>> 'Wyoming'
>>> myDict[('MD', 'ID')]
>>> '24'  #this is the FIPS code for Maryland

Even though calling this a 'one-liner' is a bit of stretch (it's 123 characters), this lets me access the entire shapefile attribute table, in this case, as a dictionary using a two-item tuple as the key.

And, I don't need to know the names or number of fields in advance. All I have to do is provide the name of the field I want to serve as the key, which is ST_ABBREV in this case.
0 Kudos
ChrisSnyder
Regular Contributor III
Update: Dictionary comprehensions now suported in Python 2.7, so an updated syntax would look like:

valueDict = {r[0]:(r[1],r[2]) for r in arcpy.da.SearchCursor(myFC, ["OID@","ID","NAME"])}
0 Kudos
DouglasSands
Occasional Contributor II
Update: Dictionary comprehensions now suported in Python 2.7, so an updated syntax would look like:

valueDict = {r[0]:(r[1],r[2]) for r in arcpy.da.SearchCursor(myFC, ["OID@","ID","NAME"])}


I use something like this to compare geometries by ID values. The above can also easily be a function (although its no longer one line)

def makeFeatureDict(myFC, keyField, fields):
    cursorFields = [keyField] + fields
    valueDict = {r[0]:[v for v in r[1:]] for r in arcpy.da.SearchCursor(myFC, cursorFields)}
    del cursorFields
    return valueDict
0 Kudos
ChrisSnyder
Regular Contributor III
How about just leaving the field values (aka the dictionary values) as a tuple? It'd be slightly faster and more memory efficient that way... Unless you need to change the values in place (have the field values be mutable), I'd go with a tuple.

def makeFeatureDict(myFC, keyField, fields):
    cursorFields = [keyField] + fields
    valueDict = {r[0]:r[1:] for r in arcpy.da.SearchCursor(myFC, cursorFields)}
    del cursorFields
    return valueDict


Also, I was curious about performace and memory use metrics for this (tuple vs list), so I ran a little test:
... 2.1 million records and 4 value fields (one as the key)
... reporting on average time and RAM use for 3 runs of each method

tuple method
23 sec @404,000k memory use

list method
27 sec @455,000k memory use
0 Kudos
DouglasSands
Occasional Contributor II
How about just leaving the field values (aka the dictionary values) as a tuple?


Yes. Was overthinking. But the whole thing can also be simplified to two lines:

def makeFeatureDict(myFC, keyField, fields):
    return {r[0]:r[1:] for r in arcpy.da.SearchCursor(myFC, [keyField] + fields)}
0 Kudos