Question

How to replace consecutive numbers in an attribute list from "1,2,3,4,7,10,13,14,15,16" to "1-4,7,10,13,14-16"?

  • 19 July 2018
  • 8 replies
  • 31 views

Badge

I have an attribute with a value of a list of numbers separated by commas. I would like for all the consecutive numbers in the list to get replaced by one dash in between the first and last consecutive number instead of having each number represented. Basically I would like this "1,2,3,4,5,7,9,14,15" to become this "1-5,7,9,14-15".


8 replies

Badge +16

Hi

You can modify this Python function, as-is it returns a list of lists.

>>> runs([1,2,3,4,7,10,13,14,15,16])

 

[[1, 2, 3, 4], [7], [10], [13, 14, 15, 16]]
  # Function to build runs of OID values
def runs(oidList): 
    runs = []
    for oid in oidList:
        if not runs:
            runs.append([oid])
            continue
        if runs[-1][-1] == oid - 1:
                runs[-1].append(oid)
        else:
            runs.append([oid])
    return runs
Badge
I have attached a sample of the data to work with. The attribute values I would like to modify are the Legal_Sub values. data.zip

 

 

Userlevel 2
Badge +17

Hi

You can modify this Python function, as-is it returns a list of lists.

>>> runs([1,2,3,4,7,10,13,14,15,16])

 

[[1, 2, 3, 4], [7], [10], [13, 14, 15, 16]]
  # Function to build runs of OID values
def runs(oidList): 
    runs = []
    for oid in oidList:
        if not runs:
            runs.append([oid])
            continue
        if runs[-1][-1] == oid - 1:
                runs[-1].append(oid)
        else:
            runs.append([oid])
    return runs
This is a PythonCaller script example using the "runs" function provided by @bruceharold.

 

def processFeature(feature):
    sub = feature.getAttribute('Legal_Sub')
    if sub:
        s = []
        for nums in runs(sorted([int(c) for c in sub.split(',')])):
            if 2 < len(nums):
                s.append('%s-%s' % (nums[0], nums[-1]))
            else:
                s += [str(n) for n in nums]
        feature.setAttribute('Legal_Sub', ','.join(s))
        
def runs(oidList):
    <omitted below>
Badge

Hi

You can modify this Python function, as-is it returns a list of lists.

>>> runs([1,2,3,4,7,10,13,14,15,16])

 

[[1, 2, 3, 4], [7], [10], [13, 14, 15, 16]]
  # Function to build runs of OID values
def runs(oidList): 
    runs = []
    for oid in oidList:
        if not runs:
            runs.append([oid])
            continue
        if runs[-1][-1] == oid - 1:
                runs[-1].append(oid)
        else:
            runs.append([oid])
    return runs
Do I need to have the "sorted" in there? The list is already sorted through a ListSort transformer. I'm having difficulty incorporating the Python in FME also, do I use Python Caller twice? One for each function?  Apologies, am new to working with Python inside FME.

 

 

Badge +16
Do I need to have the "sorted" in there? The list is already sorted through a ListSort transformer. I'm having difficulty incorporating the Python in FME also, do I use Python Caller twice? One for each function? Apologies, am new to working with Python inside FME.

 

 

Thanks Takashi, I was a bit lazy. @krenty

 

having 'sorted' in there is just good practice in case the code is used with an unsorted list.

 

Userlevel 4
Badge +13
RE: PythonCaller -- @krenty -- just put down a single PythonCaller in your flow and then paste in all the code of Takashi's as the script (and of course also including the "runs" function of Bruce's.

 

 

Cool result. Wanted to check it out so I build a sample workspace. The main trick is to tell the PythonCaller the name of the function you want it to use (in this case, processFeature). See attached: itemstoranges.fmw

 

Userlevel 4
Badge +13
Do I need to have the "sorted" in there? The list is already sorted through a ListSort transformer. I'm having difficulty incorporating the Python in FME also, do I use Python Caller twice? One for each function? Apologies, am new to working with Python inside FME.

 

 

RE: PythonCaller -- @krenty -- just put down a single PythonCaller in your flow and then paste in all the code of Takashi's as the script (and of course also including the "runs" function of Bruce's.

 

 

Cool result. Wanted to check it out so I build a sample workspace. The main trick is to tell the PythonCaller the name of the function you want it to use (in this case, processFeature). See attached: itemstoranges.fmw

 

Userlevel 2
Badge +17

I would adopt the Python scripting approach as a practical solution, but this may also be an interesting exercise using geometric operations. e.g.

Reply