Subindexing list, functional style


In [1]:
import random

The issue is the following: we have a list where one item is a category, and another one is a sortable value. We can easily sort the list appropriately, but we would also like to have a rank function, telling us the place of an item in any category.


In [12]:
mylist = [(random.randint(1,3),random.randint(1,100)) for x in range(20)]
mylist.sort()
mylist


Out[12]:
[(1, 52),
 (1, 77),
 (2, 2),
 (2, 4),
 (2, 8),
 (2, 11),
 (2, 53),
 (2, 79),
 (2, 99),
 (3, 12),
 (3, 29),
 (3, 35),
 (3, 49),
 (3, 51),
 (3, 52),
 (3, 59),
 (3, 69),
 (3, 90),
 (3, 93),
 (3, 95)]

This rank function is easy to do procedural style but not that easy functional style. It can be done - we introduce a counter function with an appropriate reset


In [14]:
def new_counter ():
    """ factory function for side-effect counter: count & reset on parameter changes
    
    assume we have 
    
        counter = new_counter()
        
    then counter(1) would yield 1,2,3....
    however, as soon as the parameter of counter changes, the counter is reset. For example
    
        counter = new_counter()
        counter(1) # 1
        counter(1) # 2
        counter(1) # 3
        counter(1) # 4
        counter(2) # 1
        counter(2) # 2
        ...
        
    different counter generated from the same factory are independet
    
        counter_a = new_counter()
        counter_b = new_counter()
        counter_a(1) # 1
        counter_b(1) # 1
        counter_a(1) # 2
        counter_b(1) # 2

    """
    
    counter = 0
    old_val = 0
    
    def f(new_val):
        
        nonlocal counter
        nonlocal old_val
        
        if old_val != new_val: 
            counter = 0
            old_val = new_val
            
        counter +=1
        return counter

    return f

In [15]:
counter = new_counter()
mylist = [ (x[0],counter(x[0]),x[1]) for x in mylist]
mylist


Out[15]:
[(1, 1, 52),
 (1, 2, 77),
 (2, 1, 2),
 (2, 2, 4),
 (2, 3, 8),
 (2, 4, 11),
 (2, 5, 53),
 (2, 6, 79),
 (2, 7, 99),
 (3, 1, 12),
 (3, 2, 29),
 (3, 3, 35),
 (3, 4, 49),
 (3, 5, 51),
 (3, 6, 52),
 (3, 7, 59),
 (3, 8, 69),
 (3, 9, 90),
 (3, 10, 93),
 (3, 11, 95)]

In [ ]: