Select to view content in your preferred language

Merge features and keep/transfer attributes

1024
6
10-05-2020 09:40 AM
2Quiker
Frequent Contributor

I have the following code that was provided by bixb0012 , thank you for the merge code. I need to be able to keep a certain fields attributes/ or transfer them once the merge is done. With the following code some merged features have info in the Field2 but some doesn't.  I would like to merge the features that have the same attribute from Field1 but keep/add what is in Field 2 to the new merged Field2.

Original features                                       Merged Features

ObjectID   Field1   Field 2            -->      ObjectID   Field1      Field2

01            CP0001   Conditional    -->            1         CP0001   Conditional

02            CP0001   'Blank'            

03            CP0001   'Blank'          

04            CP0001   'Blank'            

01            CP0014   Relocation    -->            2         CP0014   Relocation

02            CP0014   'Blank'            

03            CP0014   'Blank'            

04            CP0014   'Blank'         

from arcpy import da
from itertools import groupby
from operator import itemgetter
from functools import reduce

fc = # path to feature class or table
case_fields = ["field1", "field2"] # field(s) to group records for merging
sort_field, sort_order = "OBJECTID", "ASC"
shape_field = "SHAPE@"

fields = case_fields + [sort_field, shape_field]
sql_orderby = "ORDER BY {}, {} {}".format(", ".join(case_fields), sort_field, sort_order)

kwargs = {"in_table":fc, "field_names":fields, "sql_clause":(None, sql_orderby)}
with da.UpdateCursor(**kwargs) as ucur, da.SearchCursor(**kwargs) as scur:
    case_func = itemgetter(*range(len(case_fields)))
    ugroupby, sgroupby = groupby(ucur, case_func), groupby(scur, case_func)
    for (ukey,ugroup),(skey,sgroup) in zip(ugroupby,sgroupby):
        poly = reduce(arcpy.Geometry.union, (row[-1] for row in sgroup))
        row = next(ugroup)
        ucur.updateRow(row[:-1] + [poly])
        for row in ugroup:
            ucur.deleteRow()
0 Kudos
6 Replies
JoshuaBixby
MVP Esteemed Contributor

When you show Field2 as 'Blank', is it literally the string 'Blank' or is it an empty string or Null?

Can Field2 have multiple non-empty/blank/null values for a given value of Field1?  For example,

ObjectID   Field1   Field 2            -->      ObjectID   Field1      Field2

01            CP0001   Conditional    -->            1         CP0001   Conditional

02            CP0001   'Blank'            

03            CP0001   'Blank'          

04            CP0001   'Blank'    

If CP0001 has a value of 'Conditional' can there only be 'Blank' or other 'Conditional' values in Field2 for CP001?

0 Kudos
2Quiker
Frequent Contributor

Sorry, they are Null.

Correct - If CP0001 has a value of 'Conditional' can there only be 'Blank' or other 'Conditional' values in Field2 for CP001?

Correct - Can Field2 have multiple non-empty/blank/null values for a given value of Field1?

There are some that are not duplicates, like below.

ObjectID   Field1   Field 2                              -->      ObjectID   Field1      Field2

01            CP0001   Conditional                      -->            1         CP0001   Conditional

02            CP0001   Conditional           

03            CP0001   Null       

04            CP0001   Null   

05            CP0003   New                               --->              5      CP00003     New

06            CP0009   Old                                 -->               7      CP20009     Old

07            CP0011    AD Decision                   -->              8      CP0011       AD Decision

08            CP0011    Null                

09            CP0011    Null                                  

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

I made some changes to the inner most loop, try this:

    for (ukey,ugroup),(skey,sgroup) in zip(ugroupby,sgroupby):
        flds = list(zip(*sgroup))
        poly = reduce(arcpy.Geometry.union, flds[-1])
        fld2 = ",".join(set(flds[1]) - set([None]))
        
        row = next(ugroup)
        row[1] = fld2
        row[-1] = poly
        ucur.updateRow(row)
        for row in ugroup:
            ucur.deleteRow()
0 Kudos
2Quiker
Frequent Contributor

Thank you for the response, with the change you provided it looks like it skipped the ones with the same P_ID that had at least one blank/null in Field1 but joined the ones with the same P_ID that had Field1 populated for all those records. Also I just noticed that some Duplicate P_ID don't have anything in Field1 and if that is the case I would like Field1 populated with Field2, for example WI2018-0137. I have attached the data to reference. I really appreciate you help.

0 Kudos
JoshuaBixby
MVP Esteemed Contributor

It's good you included some sample data because your example doesn't quite explain it completely.

Are you working natively with shape files or feature classes?  I ask because your text fields don't contain any NULLs in the sample data you uploaded, which is expected for shape files, but you mentioned above they have some NULLs.

When a P_ID has no records that contain a non-NULL/non-empty Field1, how do you want it handled?  Is it possible for Field1 or Field2 to have more than one value for a given P_ID?  if so, how do you want to handle that situation?

0 Kudos
2Quiker
Frequent Contributor

It would be in a geodatabase, I exported it out of the geodatabase for testing sorry about the confusion . If a P_ID has no record in field1 I would like it to populate field1 with what is in field two if there is something in field2. There will be some that don't have anything in field1 and field2 and if that is the case just pass on that records. Field1 and Field2 shouldn't have more than one value.

0 Kudos