Need help creating a function that subsets a list. This is my code so far .
def substrings(txt):
"""Creates the sequence of all subsets as a list of text strings"""
for i in range(len(txt), len(txt) 1):
for a in itertools.combinations( "a,b,c,d,e", 1):
yield functools.reduce(lambda i, a: i a ,txt)
# Main program (does not change):
import functools, itertools
for sub in substrings('abcde'):
print(sub, end=' ')
The code should return
a b c d e ab ac ad ae bc bd be cd ce de abc abd abe acd ace ade bcd bce bde cde abcd abce abde acde bcde abcde
But I keep getting
abcde abcde abcde abcde abcde abcde abcde abcde abcde
Thanks in advanced.
CodePudding user response:
- You should be looping from 1 to the length of
txt
. - The argument to
itertools.combinations()
should betxt
, not a hard-coded string (which also shouldn't contain,
), and the combination size should bei
, not 1. - Use
''.join()
to convert the combination to a string, notfunctools.reduce()
. And if you do usereduce()
, the argument should bea
, nottxt
. That's why you keep getting the whole string, not the subsets.
In general, if you have a for
loop and never use the iteration variable, something is usually wrong.
# Main program (does not change):
import itertools
def substrings(txt):
"""Creates the sequence of all subsets as a list of text strings"""
for i in range(1, len(txt) 1):
for a in itertools.combinations(txt, i):
yield ''.join(a)
for sub in substrings('abcde'):
print(sub, end=' ')
CodePudding user response:
Your range is incorrect, and also you seem to have hardcoded the input (abcde
) in your function instead of using txt
. Try this:
def substrings(txt):
"""Creates the sequence of all subsets as a list of text strings"""
for i in range(1, len(txt) 1):
for a in itertools.combinations(txt, i):
yield "".join(a)
CodePudding user response:
A slick way to do this is, which doesn't require using any higher level modules, is using binary logic:
def substrings(inp):
for i in range(1, 2 ** len(inp)):
elem = ""
for j in range(len(inp)):
if ((1 << j) & i) > 0:
elem = inp[j]
yield elem
inp = ['a', 'b', 'c', 'd', 'e']
print([x for x in substrings(inp)])
Result:
['a', 'b', 'ab', 'c', 'ac', 'bc', 'abc', 'd', 'ad', 'bd', 'abd', 'cd', 'acd', 'bcd', 'abcd', 'e', 'ae', 'be', 'abe', 'ce', 'ace', 'bce', 'abce', 'de', 'ade', 'bde', 'abde', 'cde', 'acde', 'bcde', 'abcde']
Here, we iterate over all of the numbers between 1 and two raised to the length of the input list. This produces a bitfield, where every combination of bits represents a different combination of the input strings in the list. For example, the last number, 2**<len>
, is a number that has a 1
in every bit position, representing the set with all input elements included.