ActiveState Code

Recipe 84739: n-dimensional loop iterator object


Do nested for loops on an arbitrary list of iterable things.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class nloop:
    def __init__(self,*list):
        self.list = list
        self.memo_array = [None] * len(list)
        self.iter_list = []
        exec(self.n_combo(list))
    def __getitem__(self,index):
        return self.iter_list[index]

    def n_combo(self,lists):
        zipped = zip(list(range(len(lists))), lists)
        zipdex = map((lambda x: x[0]), zipped)
        str = "for"
        cmma = ''
        fora = ''
        for z in zipdex:
            cmma = cmma + ', ' + 'i%s' % z
        cmma = cmma[1:]
        i = 0
        for z in zipdex:
            fora = fora + 'for ' + 'i%s' % z + ' in self.list[%s] ' % i
            i = i + 1
        str = str + cmma + ' in [(' + cmma + ') ' + fora + ']:'

        cmd = "self.iter_list.append((" + cmma + "))"
        return str  + cmd


if __name__ == '__main__':
    for tup in nloop(list("abcdef"),
                     list(range(1,50)),
                     list("hijklmno"),
                     list(range(1,7))):
        print tup

###########################################

class nloop:
    def __init__(self,*varg):
        self.list = list(varg)
        self.iter_list = []

        self.memo = [None] * len(self.list)
        if len(self.list):
            self.n_iter(0, self.list)

    def __getitem__(self,index):
        return self.iter_list[index]

    def n_iter(self,index,stack):
        x = stack[index]
        for i in x:
            self.memo[index] = i
            if index == len(stack) - 1:
                self.iter_list.append(tuple(self.memo))
            else:
                self.n_iter(index + 1, stack)
                

if __name__ == '__main__':
    for tup in nloop(list("abcdef"),
                     list(range(1,50)),
                     list("hijklmno"),
                     list(range(1,7))):
        print tup

Discussion

The first version might be faster.

Comments

  1. 1. At 2:29 a.m. on 8 nov 2001, Simon Watts said:

    how "for X in S" terminates. This article was indirectly useful, in highlighting that you done need to know the __len__ of the collection object S in "for X in S" loops. Just providing __getitem__(self,index) is sufficient, and throwing IndexError when it goes out of bounds.

    The reason this is useful to me, is where S is an extension object which interfaces to some other date element source, where we can get the next element, but dont know where the end is until we reach it.

  2. 2. At 6:38 a.m. on 18 apr 2004, pascal barbedor said:

    a shorter version.

    def nloop(*lol):
        l=len(lol)
        dims=map(len,lol)
        totl=reduce(lambda x,y:x*y,dims)
        i=0
        idx=[0]*l
        while totl>i:
            a=i
            i+=1
            for j in range(l-1,-1,-1):
                a,b= divmod(a,dims[j])
                idx[j]=lol[j][b]
            yield tuple(idx)
    
    
    amounts to transforming a n-dimensional array in a long vector, and
    convert the linear index to multiple index.
    

Sign in to comment