K
K
kr_ilya2020-05-23 17:06:06
Python
kr_ilya, 2020-05-23 17:06:06

How to get all combinations of elements of a string in a slice?

The code

import itertools
for i in itertools.product('ABC', repeat=3):
  print(''.join(i))

returns
AAA
AAB
AAC
ABA
ABB
ABC
ACA
ACB
ACC
BAA
BAB
BAC
BBA
BBB
BBC
BCA
BCB
BCC
CAA
CAB
CAC
CBA
CBB
CBC
CCA
CCB
CCC


Are there ways, say, to specify from which to which elements to receive, for example, specifying "0, 4" returned
AAB
AAC
ABA
ABB

And then, specifying 7-13 in the answer was
ACC
BAA
BAB
BAC
BBA
BBB

Well, or just so that you can get elements in order, but in groups, say first 0-4, then 4-10, then 10-13 and so on. to the last element.
It is important that the pre-complete list of generated elements is not entered into a variable, as itertools.product() initially returns, but only those from which to which they should be generated.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
S
Sergey Pankov, 2020-05-23
@kr_ilya

You have the product of the string three times over itself. A string of length 3. Total 3**3 == 27 options.
Each element can be assigned a serial number.

for i in range(27): 
    print(f'{i:2}:', i // 9 % 3, i // 3 % 3, i % 3)

The numbers after the colons are the indices in the original string.
You can now get (compute) any i-th element of your sequence:
s = 'abc'
def g(i):
    return s[i // 9 % 3], s[i // 3 % 3], s[i % 3]

for i in range(len(s) ** 3):
    print(g(i))

Can be summarized:
def g(s, i):
    n = len(s)
    return [
        s[i // n**(n-j-1) % n]
        for j in range(n)
    ]

my_custom_string = 'abc'
for i in range(len(my_custom_string) ** len(my_custom_string)):
    print(g(my_custom_string, i))

Well, since such a booze has gone, you can make your multiplication indexed:
import itertools
import functools
import operator


class Producti: 
     def __init__(self, *iters, repeat=1): 
         iters = [list(it) for it in iters]
         self.iters = iters * repeat
         self._len = functools.reduce(operator.mul, map(len, self.iters)) 

     def __len__(self): 
         return self._len 

     #def __iter__(self): 
     #    return (self[i] for i in range(len(self))) 

     def __getitem__(self, idx): 
         if isinstance(idx, slice): 
             return (self[i] for i in range(len(self))[idx])
         if idx >= len(self):
             raise IndexError(f'product index out of range')
         r = [] 
         d = 1 
         for it in self.iters: 
             r.append(it[idx // d % len(it)]) 
             d *= len(it) 
         return tuple(r[::-1]) 


for x in Producti('abc', repeat=3)[3:7]: 
     print(x)

The only thing we have to sacrifice compared to the regular product is the unpacking of element iterators. We need this in order to know in advance how to cast a number to indices.
PS
By the way, the "__iter__" method, in principle, is not needed, iter(Producti('aa', '12')) will be iterated now and so (when there is an exception on going beyond len).

D
Dr. Bacon, 2020-05-23
@bacon

1. itertools.islice will probably arrange it
2. if not, write your own function on yields
3. keep in mind that iterators are only about increasing, i.e. without intermediate storage, roll only 0-4 and 5-10, but not 0-4 and 4-10, and especially not 0-4 and 3-10

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question