Deleting Duplicate Tuples of Lists from List - python

I want to write a script to take a list of categories and return the unique ways to split the categories into 2 groups. For now I have it in tuple form (list_a, list_b) where the union of list_a and list_b represents the full list of categories.
Below I have shown with an example with categories ['A','B','C','D'], I can get all the groups. However, some are duplicates (['A'], ['B', 'C', 'D']) represents the same split as (['B', 'C', 'D'], ['A']). How do I retain only unique splits? Also what is a better title for this post?
import itertools
def getCompliment(smallList, fullList):
compliment = list()
for item in fullList:
if item not in smallList:
compliment.append(item)
return compliment
optionList = ['A','B','C','D']
combos = list()
for r in range(1,len(optionList)):
tuples = list(itertools.combinations(optionList, r))
for t in tuples:
combos.append((list(t),getCompliment(list(t), optionList)))
print(combos)
[(['A'], ['B', 'C', 'D']),
(['B'], ['A', 'C', 'D']),
(['C'], ['A', 'B', 'D']),
(['D'], ['A', 'B', 'C']),
(['A', 'B'], ['C', 'D']),
(['A', 'C'], ['B', 'D']),
(['A', 'D'], ['B', 'C']),
(['B', 'C'], ['A', 'D']),
(['B', 'D'], ['A', 'C']),
(['C', 'D'], ['A', 'B']),
(['A', 'B', 'C'], ['D']),
(['A', 'B', 'D'], ['C']),
(['A', 'C', 'D'], ['B']),
(['B', 'C', 'D'], ['A'])]
I need the following:
[(['A'], ['B', 'C', 'D']),
(['B'], ['A', 'C', 'D']),
(['C'], ['A', 'B', 'D']),
(['D'], ['A', 'B', 'C']),
(['A', 'B'], ['C', 'D']),
(['A', 'C'], ['B', 'D']),
(['A', 'D'], ['B', 'C'])]

You are very close. What you need is a set of results.
Since set elements must be hashable and list objects are not hashable, you can use tuple instead. This can be achieved by some trivial changes to your code.
import itertools
def getCompliment(smallList, fullList):
compliment = list()
for item in fullList:
if item not in smallList:
compliment.append(item)
return tuple(compliment)
optionList = ('A','B','C','D')
combos = set()
for r in range(1,len(optionList)):
tuples = list(itertools.combinations(optionList, r))
for t in tuples:
combos.add(frozenset((tuple(t), getCompliment(tuple(t), optionList))))
print(combos)
{frozenset({('A',), ('B', 'C', 'D')}),
frozenset({('A', 'C', 'D'), ('B',)}),
frozenset({('A', 'B', 'D'), ('C',)}),
frozenset({('A', 'B'), ('C', 'D')}),
frozenset({('A', 'C'), ('B', 'D')}),
frozenset({('A', 'D'), ('B', 'C')}),
frozenset({('A', 'B', 'C'), ('D',)})}
If you need to convert the result back to a list of lists, this is possible via a list comprehension:
res = [list(map(list, i)) for i in combos]
[[['A'], ['B', 'C', 'D']],
[['B'], ['A', 'C', 'D']],
[['A', 'B', 'D'], ['C']],
[['A', 'B'], ['C', 'D']],
[['B', 'D'], ['A', 'C']],
[['B', 'C'], ['A', 'D']],
[['A', 'B', 'C'], ['D']]]

Related

How to do selective combination in Python List?

I'm not really sure how to frame my question, but here's a try. I have a list of strings and tuples of strings. I want all combinations such that I pick only one value from each tuple.
It's much easier to demonstrate with an example.
Input:
x = ['a', ('b', 'c'), ('d', 'e'), 'f']
Output:
y = [
['a', 'b', 'd', 'f'],
['a', 'c', 'd', 'f'],
['a', 'b', 'e', 'f'],
['a', 'c', 'e', 'f']
]
Example 2:
Input:
x = ['a', ('b', 'c'), ('d', 'e'), 'f', ('g', 'h')]
Output:
y = [
['a', 'b', 'd', 'f', 'g'],
['a', 'c', 'd', 'f', 'g'],
['a', 'b', 'e', 'f', 'g'],
['a', 'c', 'e', 'f', 'g'],
['a', 'b', 'd', 'f', 'h'],
['a', 'c', 'd', 'f', 'h'],
['a', 'b', 'e', 'f', 'h'],
['a', 'c', 'e', 'f', 'h']
]
x = ['a', ('b', 'c'), ('d', 'e'), 'f', ('g', 'h')]
First normalize your input
x = [tuple(xx) for xx in x if not isinstance(x, tuple)]
Then:
import iterools
list(itertools.product(*x))
In the output your have a list of tuples, it should be very easy to get list of list as you want.
Actually the normalization step is not necessary.

How do you split a string into different sections inside a list?

For example, I have a list:
[['aabbbb'], ['bbbbab'], ['babbab'], ['baaaaa'], ['bbbaaa'], ['bbbbaa']]
how do I split it so that I get [['a', 'a', 'b', 'b', 'b', 'b'],... etc? It would be very useful thanks!
You can use list comprehension.
mylist = [['aabbbb'], ['bbbbab'], ['babbab'], ['baaaaa'], ['bbbaaa'], ['bbbbaa']]
new_list = [list(item[0]) for item in mylist]
This will return,
[['a', 'a', 'b', 'b', 'b', 'b'], ['b', 'b', 'b', 'b', 'a', 'b'], ['b', 'a', 'b', 'b', 'a', 'b'], ['b', 'a', 'a', 'a', 'a', 'a'], ['b', 'b', 'b', 'a', 'a', 'a'], ['b', 'b', 'b', 'b', 'a', 'a']]
Try the code below
oldList = [['aabbbb'], ['bbbbab'], ['babbab'], ['baaaaa'], ['bbbaaa'], ['bbbbaa']]
newList = []
for i in oldList:
newList.append(list(i[0]))

Permutation program issue, has something to do with global variables (Python)

this is an algorithm that is meant to write down some of the permutations of the list P and it does it well, but...
def p():
global P
P = ['a', 'b', 'c', 'd']
perm(4)
per = []
def perm(k):
global P
if k==1:
print(P)
per.append(P)
else:
for i in range(k):
P[i], P[k-1] = P[k-1], P[i]
perm(k-1)
P[i], P[k-1] = P[k-1], P[i]
when i want it to add the permutations to a global list (necessary for the rest of the program) there is a problem. It still prints all the permutations
['b', 'c', 'd', 'a']
['b', 'c', 'd', 'a']
['d', 'b', 'c', 'a']
['d', 'b', 'c', 'a']
['b', 'd', 'c', 'a']
['b', 'd', 'c', 'a']
['a', 'c', 'b', 'd']
['a', 'c', 'b', 'd']
['b', 'a', 'c', 'd']
['b', 'a', 'c', 'd']
['a', 'b', 'c', 'd']
['a', 'b', 'c', 'd']
['b', 'd', 'a', 'c']
['b', 'd', 'a', 'c']
['a', 'b', 'd', 'c']
['a', 'b', 'd', 'c']
['b', 'a', 'd', 'c']
['b', 'a', 'd', 'c']
['a', 'd', 'b', 'c']
['a', 'd', 'b', 'c']
['b', 'a', 'd', 'c']
['b', 'a', 'd', 'c']
['a', 'b', 'd', 'c']
['a', 'b', 'd', 'c']
but when i check the list it's filled with the default set
[['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b',
'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'],
['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b',
'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'],
['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b',
'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'],
['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c'], ['a', 'b', 'd', 'c']]
could you please help me at least with what's the actual issue?
Try using built-in itertools link .
import itertools
P = [letter for letter in "abcd"]
def perm(permutate_this):
return list(itertools.permutations(permutate_this))
print(perm(P))
Use
per.append(P[:])
to copy the list. You are appending a reference to a list and thats always the same data. Your per contains the same reference over and over.
Comment-remark of cdarke:
Slicing a list is calles a shallow copy - if you have lists that contain other refs (f.e. inner lists) it will only copy the ref and you have the same problem for the inner lists - you would have to resort to copy.deepcopy in that case.
Example:
innerlist = [1,2,3]
l2 = [innerlist, 5, 6]
l3 = l2[:]
print(l2) # orig
print(l3) # the shallow copy
l3[2] = "changed" # l2[2] is unchanged
print(l2)
print(l3)
innerlist[2] = 999 # both (l2 and l3) will reflect this change in the innerlist
print(l2)
print(l3)
Output:
[[1, 2, 3], 5, 6] # l2
[[1, 2, 3], 5, 6] # l3
[[1, 2, 3], 5, 6] # l2 unchanged by l3[2]='changed'
[[1, 2, 3], 5, 'changed'] # l3 changed by -"-
[[1, 2, 999], 5, 6] # l2 and l3 affected by change in innerlist
[[1, 2, 999], 5, 'changed']
It may be more convenient for you to use built-in permutations:
from itertools import permutations
arr = [1, 2, 3]
list(permutations(arr))
> [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]

Python: merging lists like this

I have many troubles to merge two simple nested lists while keeping the overall structure. For example:
# From this source:
x = [['a','b','c'],['d','e','f'],['g','h','i']]
y = [['A','B','C'],['D','E','F'],['G','H','I']]
# To this result:
z = [[['a','A'],['b','B'],['c','C']],
[['d','D'],['e','E'],['f','F']],
[['g','G'],['h','H'],['i','I']]]
Any idea?
Here, some (embarrassing) code I tried:
X = []
Y = []
for i in iter(x[:]):
X.append('=')
for v in iter(i[:]):
X.append(v)
print X; print
for i in iter(y[:]):
Y.append('=')
for v in iter(i[:]):
Y.append(v)
print Y; print
for i in zip(X, Y):
print i
print([list(map(list,zip(*t))) for t in zip(x,y)])
[[['a', 'A'], ['b', 'B'], ['c', 'C']],
[['d', 'D'], ['e', 'E'], ['f', 'F']],
[['g', 'G'], ['h', 'H'], ['i', 'I']]]
The steps:
In [20]: zip(x,y) # zip the lists together
Out[20]:
[(['a', 'b', 'c'], ['A', 'B', 'C']),
(['d', 'e', 'f'], ['D', 'E', 'F']),
(['g', 'h', 'i'], ['G', 'H', 'I'])]
In [21]: t = (['a', 'b', 'c'], ['A', 'B', 'C']) # first "t"
In [22]: zip(*t) # transpose, row to columns, columns to rows
Out[22]: [('a', 'A'), ('b', 'B'), ('c', 'C')]
In [23]: list(map(list,zip(*t))) # convert inner tuples to lists
Out[23]: [['a', 'A'], ['b', 'B'], ['c', 'C']]

Extracting Unique String Combinations from List of List in Python

I'm trying to extract all the unique combinations of strings from a list of lists in Python. For example, in the code below, ['a', 'b','c'] and ['b', 'a', 'c'] are not unique, while ['a', 'b','c'] and ['a', 'e','f'] or ['a', 'b','c'] and ['d', 'e','f'] are unique.
I've tried converting my list of lists to a list of tuples and using sets to compare elements, but all elements are still being returned.
combos = [['a', 'b', 'c'], ['c', 'b', 'a'], ['d', 'e', 'f'], ['c', 'a', 'b'], ['c', 'f', 'b']]
# converting list of list to list of tuples, so they can be converted into a set
combos = [tuple(item) for item in combos]
combos = set(combos)
grouping_list = set()
for combination in combos:
if combination not in grouping_list:
grouping_list.add(combination)
##
print grouping_list
>>> set([('a', 'b', 'c'), ('c', 'a', 'b'), ('d', 'e', 'f'), ('c', 'b', 'a'), ('c', 'f', 'b')])
How about sorting, (and using a Counter)?
from collections import Counter
combos = [['a', 'b', 'c'], ['c', 'b', 'a'], ['d', 'e', 'f'], ['c', 'a', 'b'], ['c', 'f', 'b']]
combos = Counter(tuple(sorted(item)) for item in combos)
print(combos)
returns:
Counter({('a', 'b', 'c'): 3, ('d', 'e', 'f'): 1, ('b', 'c', 'f'): 1})
EDIT: I'm not sure if I'm correctly understanding your question. You can use a Counter to count occurances, or use a set if you're only interested in the resulting sets of items, and not in their uniqueness.
Something like:
combos = set(tuple(sorted(item)) for item in combos)
Simply returns
set([('a', 'b', 'c'), ('d', 'e', 'f'), ('b', 'c', 'f')])
>>> set(tuple(set(combo)) for combo in combos)
{('a', 'c', 'b'), ('c', 'b', 'f'), ('e', 'd', 'f')}
Simple but if we have same elements in the combo, it will return wrong answer. Then, sorting is the way to go as suggested in others.
How about this:
combos = [['a', 'b', 'c'], ['c', 'b', 'a'], ['d', 'e', 'f'], ['c', 'a', 'b'], ['c', 'f', 'b']]
print [list(y) for y in set([''.join(sorted(c)) for c in combos])]

Categories