I won't claim to understand every part of my solution because a large chunk of code in the middle comes from a Q&A that I initiated at Stack Overflow: https://stackoverflow.com/questions/49396943
The solution code starts by creating some test data which is illustrated by labelling its polygons with their OBJECTIDs:

import arcpy
# Create test data
if arcpy.Exists("C:/Temp/test.gdb"):
arcpy.Delete_management("C:/Temp/test.gdb")
arcpy.CreateFileGDB_management("C:/Temp","test")
arcpy.CreateFishnet_management("C:/Temp/test.gdb/testFishnet",
"0 0","0 1","1","1","5","5",
labels="NO_LABELS",
geometry_type="POLYGON")
arcpy.Select_analysis("C:/Temp/test.gdb/testFishnet",
"C:/Temp/test.gdb/testFC",
"OID IN (1,7,13,19,25,16,21,22,4,5,10)")
# Add field that will hold group numbers and will eventually be dissolved on
arcpy.AddField_management("C:/Temp/test.gdb/testFC","DISS","LONG")
# Write neighbourhood relationships to a table
arcpy.PolygonNeighbors_analysis("C:/Temp/test.gdb/testFC",
"C:/Temp/test.gdb/testNeighbors",
"OBJECTID","NO_AREA_OVERLAP",
"BOTH_SIDES")
# Write a dictionary with src_OBJECTID as its keys and a list of
# nbr_OBJECTIDs as its values
d = {}
with arcpy.da.SearchCursor("C:/Temp/test.gdb/testNeighbors",
["src_OBJECTID","nbr_OBJECTID"]) as cursor:
for row in cursor:
if row[0] in d:
d[row[0]] = d[row[0]] + [row[1]]
else:
d[row[0]] = [row[1]]
print(d)
# For the test data the dictionary (d) looks like this
# {1: [4], 2: [3, 5], 3: [2, 5], 4: [1, 6], 5: [2, 3],
# 6: [4, 8], 7: [9, 10], 8: [6, 11], 9: [7, 10], 10: [7, 9], 11: [8]}
# Code below here comes from https://stackoverflow.com/a/49397534/820534
# with minor modification because naming groups with integers rather
# letters is easier
#
class MSet(object):
def __init__(self, p):
self.val = p
self.p = self
self.rank = 0
def parent_of(x): # recursively find the parents of x
if x.p == x:
return x.val
else:
return parent_of(x.p)
def make_set(x):
return MSet(x)
def find_set(x):
if x != x.p:
x.p = find_set(x.p)
return x.p
def link(x,y):
if x.rank > y.rank:
y.p = x
else:
x.p = y
if x.rank == y.rank:
y.rank += 1
def union(x,y):
link(find_set(x), find_set(y))
vertices = {k: make_set(k) for k in d.keys()}
edges = []
for k,u in vertices.items():
for v in d[k]:
edges.append((u,vertices[v]))
# do disjoint set union find similar to kruskal's algorithm
for u,v in edges:
if find_set(u) != find_set(v):
union(u,v)
# resolve the root of each disjoint set
parents = {}
# generate set of parents
set_parents = set()
for u,v in edges:
set_parents |= {parent_of(u)}
set_parents |= {parent_of(v)}
# make a mapping from only parents to A-Z, to allow up to 26 disjoint sets
# letters = {k : chr(v) for k,v in zip(set_parents, list(range(65,91)))}
for u,v in edges:
# parents[u.val] = letters[parent_of(u)]
# parents[v.val] = letters[parent_of(v)]
parents[u.val] = parent_of(u)
parents[v.val] = parent_of(v)
print(parents)
# End of code from https://stackoverflow.com/a/49397534/820534
# For the test data the dictionary (parents) looks like this
# {1: 4, 2: 3, 3: 3, 4: 4, 5: 3, 6: 4, 7: 9, 8: 4, 9: 9, 10: 9, 11: 4}
# Read the parents dictionary and update the dissolve field
# in the original feature class
with arcpy.da.UpdateCursor("C:/Temp/test.gdb/testFC",
["OBJECTID","DISS"]) as cursor:
for row in cursor:
row[1] = parents[row[0]]
cursor.updateRow(row)
# Dissolve on the dissolve field (DISS)
arcpy.Dissolve_management("C:/Temp/test.gdb/testFC",
"C:/Temp/test.gdb/testDiss",
"DISS","","MULTI_PART","DISSOLVE_LINES")
The result of running the code is a feature class dissolved on the group identifiers that it assigns based on whether input features touch by at least a node.
