Creating minimal groups from two lists - python

I have a tuple containing a letter/name and a list of chars. I also have a list of purely chars. The idea is that I want to create new lists containing all the possible combinations of the letter/name from the tuple. Where their respective char list contains all the chars which is in the other char list. However this new list should be minimal, meaning there shouldn't be any redudant letter/names. An example,
I have the following tuples,
('C', ['l'])
('D', ['l', 'q'])
('E', ['q', 's'])
('F', ['s'])
('H', ['q', 's'])
I should mention that the tuples are in a list aswell. So a list of tuples
The input list is, ['l', 'q', 's']
In this example I should get the following lists,
['C', 'E']
['C', 'H']
['D', 'E']
['D', 'F']
['D', 'H']
Because 'C' and 'E' combined gives ['l', 'q', 's'] and so forth. It should not return ['C', 'F', 'H'] Because here 'F' would be redundant.
My first thought was that I would check the result of appending each of them together. However this would not work for a larger example where the return list is larger than two chars.

This worked for me:
list_tuples = [('C', ['l']), ('D', ['l', 'q']),
('E', ['q', 's']), ('F', ['s']), ('H', ['q', 's'])]
input_list = ['l', 'q', 's']
out_list = []
for i in range(len(list_tuples)):
for one_tuple in list_tuples:
if list_tuples[i][0] != one_tuple[0]:
to_compare_list = list_tuples[i][1] + one_tuple[1]
counter = 0
for one_letter in input_list:
if one_letter in to_compare_list:
counter += 1
if counter >= len(input_list):
out_list.append([list_tuples[i][0], one_tuple[0]])
out_list = out_list[:int(len(out_list) / 2)]
print(out_list)
Output:
[['C', 'E'], ['C', 'H'], ['D', 'E'], ['D', 'F'], ['D', 'H']]
Please note that this code is not efficient, for better results refer to the itertools library in python that counts with permutation and combination modules.
EDIT
A corrected and more general solution:
import itertools
import copy
list_tuples = [('A', ['a', 'b', 'd', 'e']), ('B', ['d', 'e', 'i']), ('C', ['i', 'l', 'n']), ('D', ['l', 'n', 'o', 'r']), ('E', [
'n', 'o', 'r', 's', 't']), ('F', ['a', 'b', 'r', 's', 't']), ('G', ['a', 'e', 'i', 'o']), ('H', ['b', 'e', 's'])]
input_list = ['a', 'b', 'd', 'e', 'i', 'l', 'n', 'o', 'r', 's', 't']
dic_of_list_tuples = dict(list_tuples)
keys = dic_of_list_tuples.keys()
out_list = []
posible_combinations = []
letter_space = []
for i in range(len(keys)):
posible_combinations += list(itertools.combinations(keys, i + 1))
for one_combinations in posible_combinations:
for one_letter in one_combinations:
letter_space += dic_of_list_tuples[one_letter]
if (all(item in letter_space for item in input_list)):
out_list.append(list(one_combinations))
letter_space = []
filter_out_list = copy.deepcopy(out_list)
for one_list in out_list:
for i in range(len(out_list)):
if (one_list != out_list[i]) and (all(item in out_list[i] for item in one_list)):
try:
filter_out_list.remove(out_list[i])
except ValueError:
pass
print(filter_out_list)
Output:
[['A', 'C', 'E'], ['B', 'D', 'F'], ['A', 'B', 'D', 'E'], ['A', 'C', 'D', 'F'], ['A', 'C', 'F', 'G'], ['A', 'D', 'E', 'G'], ['A', 'D', 'F', 'G'], ['B', 'C', 'E', 'F'], ['B', 'C', 'F', 'G'], ['B', 'C', 'E', 'G', 'H'], ['B', 'D', 'E', 'G', 'H']]

Related

How do I record the history of what happened while using sort in python for one string, and apply that to other strings?

with open(sys.argv[1]) as f:
lst = list(f.readline().strip())
sortedLst = sorted(lst, key = lambda x: (x.lower(), x.swapcase()))
print(lst)
print(sortedLst)
The word I am using as an example is 'ThatCcer'.
My outputs are ['T', 'h', 'a', 't', 'C', 'c', 'e', 'r'] for lst and my outputs are ['a', 'c', 'C', 'e', 'h', 'r', 't', 'T'] for sortedLst.
This is exactly what I am going for - to sort a word in alphabetical order with lower case letters taking precedence over upper case.
What I am trying to achieve is to match other 8-letter inputs by sorting them in the exact way that I have sorted ThatCcher. How would I go about achieving this?
EDIT: I am being told the question is unclear - my apologies but it is a bit difficult to explain so I will try again.
By sorting ThatCcer to become acCehrtT, lst[0] ('T') took the position of sortedLst[7], lst[1] ('h') took the position of sortedLst[4], and so on...
This is the history I want to record and so that given any other string can copy the steps that 'ThatCcer' took, for example: s = ['h', 'o', 'w', 'e', 'v', 'e', 'r', 's'] I want s[0] to to take its' position in sortedS[7], just like ThatCcer did.
I hope this made it a little clearer!
IIUC, you want to achieve a behavior similar to that of numpy.argsort.
You can sort a range based on your criteria, and use it to reindex any string:
lst = ['T', 'h', 'a', 't', 'C', 'c', 'e', 'r']
idx = list(range(len(lst)))
sorted_idx = sorted(idx, key=lambda x: (lst[x].lower(), lst[x].swapcase()))
# [2, 5, 4, 6, 1, 7, 3, 0]
# now use the index to sort
[lst[i] for i in sorted_idx]
# ['a', 'c', 'C', 'e', 'h', 'r', 't', 'T']
# keep the same order on another string
lst2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
[lst2[i] for i in sorted_idx]
# ['c', 'f', 'e', 'g', 'b', 'h', 'd', 'a']
Another approach using zip:
lst = ['T', 'h', 'a', 't', 'C', 'c', 'e', 'r']
lst2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
list(zip(*sorted(zip(lst, lst2), key=lambda x: (x[0].lower(), x[0].swapcase()))))
# or as individual lists
# (lst_sorted, lst2_sorted) = list(zip(*sorted(zip(lst, lst2),
# key=lambda x: # (x[0].lower(), x[0].swapcase()))))
output:
[('a', 'c', 'C', 'e', 'h', 'r', 't', 'T'),
('c', 'f', 'e', 'g', 'b', 'h', 'd', 'a')]
Sort the enumerated string on the string characters, then separate the (sorted) indices and characters; use operator.itemgetter to create a callable that you can re-use.
import operator
def f(thing):
s_lst = sorted(enumerate(thing),key = lambda x: (x[1].lower(), x[1].swapcase()))
argsort = operator.itemgetter(*[x[0] for x in s_lst])
s_lst = [x[1] for x in s_lst]
return s_lst,argsort
>>> s_lst, argsort = f('ThatCcerCTaa')
>>> s_lst
['a', 'a', 'a', 'c', 'C', 'C', 'e', 'h', 'r', 't', 'T', 'T']
>>> argsort('ThatCcerCTaa')
('a', 'a', 'a', 'c', 'C', 'C', 'e', 'h', 'r', 't', 'T', 'T')
>>> argsort
operator.itemgetter(2, 10, 11, 5, 4, 8, 6, 1, 7, 3, 0, 9)
>>>

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.

Python - Creating permutations with output array index constraints

I want to create all possible permutations for an array in which each element can only occur once, with constraints on the element array index position.
ID = ["A","B","C","D","E","F","G","H","I","J"]
I want to create all possible permutations of the original_array, however the positions of each element are restricted to index positions given by:
ID = ["A","B","C","D","E","F","G","H","I","J"]
Index_Options=[]
for i in range(len(ID)):
List1=[]
distance=3
value = i - distance
for j in range((int(distance)*2)):
if value < 0 or value > len(ID):
print("Disregard") #Outside acceptable distance range
else:
List1.append(value)
value=value+1
Index_Options.append(List1)
print(Index_Options)
#Index_Options gives the possible index positions for each element. ie "A" can occur in only index positions 0,1,2, "B" can occur in only index positions 0,1,2,3 ect.
I'm just struggling on how to then use this information to create all the output permutations.
Any help would be appreciated
You can use a recursive generator function to build the combinations. Instead of generating all possible permutations from ID and then filtering based on Index_Options, it is much more efficient to produce a cartesian product of ID by directly traversing Index_Options:
ID = ["A","B","C","D","E","F","G","H","I","J"]
def combos(d, c = [], s = []):
if not d:
yield c
else:
for i in filter(lambda x:x not in s and x < len(ID), d[0]):
yield from combos(d[1:], c=c+[ID[i]], s=s+[i])
print(list(combos(Index_Options)))
Output (first ten combinations produced):
[['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'I'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'I', 'H', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'I', 'J', 'H'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'J', 'H', 'I'], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'J', 'I', 'H'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'G', 'I', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'G', 'J', 'I'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'I', 'G', 'J'], ['A', 'B', 'C', 'D', 'E', 'F', 'H', 'I', 'J', 'G']]
You can use itertools.permutations to create all the possible permutations and then create new list with a check if all the letters are in the correct position
permutations = [p for p in itertools.permutations(ID, len(ID)) if all(i in Index_Options[ID.index(x)] for i, x in enumerate(p))]

Is there a pythonic way of permuting a list of list?

I have a list of lists containing unique strings and I want to produce an arbitrary number of different ways of sorting it. The list might look like the following:
list = [[a], [b,c], [d], [e,f,g]]
The order of the lists need to be the same but I want to shuffle the ordering within a list and then have them in a single list, e.g
list1 = [a,b,c,d,e,f,g]
list2 = [a,c,b,d,f,e,g]
...
...
listN = [a,c,b,d,f,g,e]
What is a good pythonic way of achieving this? I'm on python 2.7.
from itertools import permutations, product
L = [['a'], ['b','c'], ['d'], ['e', 'f', 'g']]
for l in product(*map(lambda l: permutations(l), L)):
print([item for s in l for item in s])
produces:
['a', 'b', 'c', 'd', 'e', 'f', 'g']
['a', 'b', 'c', 'd', 'e', 'g', 'f']
['a', 'b', 'c', 'd', 'f', 'e', 'g']
['a', 'b', 'c', 'd', 'f', 'g', 'e']
['a', 'b', 'c', 'd', 'g', 'e', 'f']
['a', 'b', 'c', 'd', 'g', 'f', 'e']
['a', 'c', 'b', 'd', 'e', 'f', 'g']
['a', 'c', 'b', 'd', 'e', 'g', 'f']
['a', 'c', 'b', 'd', 'f', 'e', 'g']
['a', 'c', 'b', 'd', 'f', 'g', 'e']
['a', 'c', 'b', 'd', 'g', 'e', 'f']
['a', 'c', 'b', 'd', 'g', 'f', 'e']
You can do this by taking the Cartesian product of the permutations of the sub-lists, and then flattening the resulting nested tuples.
from itertools import permutations, product, chain
lst = [['a'], ['b', 'c'], ['d'], ['e', 'f', 'g']]
for t in product(*[permutations(u) for u in lst]):
print([*chain.from_iterable(t)])
output
['a', 'b', 'c', 'd', 'e', 'f', 'g']
['a', 'b', 'c', 'd', 'e', 'g', 'f']
['a', 'b', 'c', 'd', 'f', 'e', 'g']
['a', 'b', 'c', 'd', 'f', 'g', 'e']
['a', 'b', 'c', 'd', 'g', 'e', 'f']
['a', 'b', 'c', 'd', 'g', 'f', 'e']
['a', 'c', 'b', 'd', 'e', 'f', 'g']
['a', 'c', 'b', 'd', 'e', 'g', 'f']
['a', 'c', 'b', 'd', 'f', 'e', 'g']
['a', 'c', 'b', 'd', 'f', 'g', 'e']
['a', 'c', 'b', 'd', 'g', 'e', 'f']
['a', 'c', 'b', 'd', 'g', 'f', 'e']
If you need to do this in Python 2, you can replace the print line with this:
print list(chain.from_iterable(t))
Here's a more compact version, inspired by ewcz's answer:
for t in product(*map(permutations, lst)):
print list(chain.from_iterable(t))
This might not be the most elegant solution, but I think it does what you want
from itertools import permutations
import numpy as np
def fac(n):
if n<=1:
return 1
else:
return n * fac(n-1)
lists = [['a'], ['b','c'], ['d'], ['e','f','g']]
combined = [[]]
for perm in [permutations(l,r=len(l)) for l in lists]:
expanded = []
for e in list(perm):
expanded += [list(l) + list(e) for l in combined]
combined = expanded
## check length
print np.prod(map(fac,map(len,lists))), len(combined)
print '\n'.join(map(str,combined))
You can flatten the list then simply generate its permutations:
from itertools import chain, permutations
li = [['a'], ['b','c'], ['d'], ['e','f','g']]
flattened = list(chain.from_iterable(li))
for perm in permutations(flattened, r=len(flattened)):
print(perm)
>> ('a', 'b', 'c', 'd', 'e', 'f', 'g')
('a', 'b', 'c', 'd', 'e', 'g', 'f')
('a', 'b', 'c', 'd', 'f', 'e', 'g')
('a', 'b', 'c', 'd', 'f', 'g', 'e')
('a', 'b', 'c', 'd', 'g', 'e', 'f')
('a', 'b', 'c', 'd', 'g', 'f', 'e')
('a', 'b', 'c', 'e', 'd', 'f', 'g')
('a', 'b', 'c', 'e', 'd', 'g', 'f')
('a', 'b', 'c', 'e', 'f', 'd', 'g')
...
...
...
from itertools import chain, permutations
your_list = [[a], [b,c], [d], [e,f,g]]
flattened = chain.from_iterable(your_list)
perms = permutations(flattened)
for perm in perms:
print perm
References:
permutations in Python 2
chain in Python 2

Sorting a list by a key

Currently working on a transposition problem. What I have so far is that a user enters a message and that message is encrypted into a list, like below:
['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC']
What I have for the next stage of the cipher is putting this into a table with a key inputted from the user. So if the user enters 'CODE' it outputs this:
2: Enter the keyword for final encryption: code
C O D E
['B', 'C', 'D', 'E']
['D', 'E', 'D', 'A']
['F', 'D', 'D', 'D']
['B', 'E', 'F', 'E']
['D', 'A', 'E', 'A']
['F', 'E', 'B', 'C']
The next stage is to take each value of each column and print the values corresponding to its alphabetical column. So my expected output would be:
C D E O
['B', 'D', 'E', 'C']
['D', 'D', 'A', 'E']
['F', 'D', 'D', 'D']
['B', 'F', 'E', 'E']
['D', 'E', 'A', 'A']
['F', 'B', 'C', 'E']
The problem I'm having is trying to know how to put each of the values in their corresponding column and printing them.
Here's what I have so far:
def encodeFinalCipher():
matrix2 = []
# Convert keyword to upper case
key = list(keyword.upper())
# Convert firstEncryption to a string
firstEncryptionString = ''.join(str(x) for x in firstEncryption)
# Print the first table that will show the firstEncryption and the keyword above it
keywordList = list(firstEncryptionString)
for x in range(0,len(keywordList),len(keyword)):
matrix2.append(list(keywordList[x:x+len(keyword)]))
# Print the un-ordered matrix to the screen
print (' %s' % ' '.join(map(str, key)))
for letters in matrix2:
print (letters)
unOrderedMatrix = [[matrix2[i][j] for i in range(len(matrix2))] for j in range(len(matrix2[0]))]
for index, item in enumerate (unOrderedMatrix):
print("\n",index, item)
index = sorted(key)
print(index)
I get the output of the sorted key:
['A', 'K', 'M', 'R']
What I would like to know is how can this sorted key be applied to the values they represent? I know I can get the first column by doing this:
print(unOrderedMatrix[0])
Which gets me the list of the first column.
Any help would be much appreciated. Complete beginner on Python
msg = ['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC', '12']
key = 'CODE'
# 'flatten' the message
msg = ''.join(msg)
key_length = len(key)
#create a dictionary with the letters of the key as the keys
#use a slice to create the values
columns = {k:msg[i::key_length] for i, k in enumerate(key)}
print columns
# sort the columns on the key letters
columns = sorted(columns.items())
print columns
# separate the key from the columnar data
header, data = zip(*columns)
print header
# transpose and print
for thing in zip(*data):
print thing
>>>
{'C': 'BDFBDF1', 'E': 'EADEAC', 'D': 'DDDFEB', 'O': 'CEDEAE2'}
[('C', 'BDFBDF1'), ('D', 'DDDFEB'), ('E', 'EADEAC'), ('O', 'CEDEAE2')]
('C', 'D', 'E', 'O')
('B', 'D', 'E', 'C')
('D', 'D', 'A', 'E')
('F', 'D', 'D', 'D')
('B', 'F', 'E', 'E')
('D', 'E', 'A', 'A')
('F', 'B', 'C', 'E')
>>>
code = raw_input("Enter the keyword for final encryption:")
user_input = ['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC']
user_input = ''.join(user_input)
matrix = [user_input[i:i+len(code)] for i in range(0, len(user_input), len(code))]
matrix.insert(0, code)
result = sorted([[matrix[j][ind] for j in range(len(matrix))] for ind in range(len(code)) ], key= lambda i:i[0])
for row in [[each[ind] for each in result] for ind in range(len(result[0]))]:
print row
Print row results as:
Enter the keyword for final encryption:CODE
['C', 'D', 'E', 'O']
['B', 'D', 'E', 'C']
['D', 'D', 'A', 'E']
['F', 'D', 'D', 'D']
['B', 'F', 'E', 'E']
['D', 'E', 'A', 'A']
['F', 'B', 'C', 'E']
Here is something to get you started (you may want to separate the loop one-liner to smaller bits):
# define data
data = [['B', 'C', 'D', 'E'], ['D', 'E', 'D', 'A'], ['F', 'D', 'D', 'D'], ['B', 'E', 'F', 'E'], ['D', 'A', 'E', 'A'], ['F', 'E', 'B', 'C']]
# choose code word
code = 'code'
# add original locations to code word [(0, c), (1, o), (2, d), (3, e))]
# and sort them alphabetically!
code_with_locations = list(sorted(enumerate(code)))
print code_with_locations # [(0, 'c'), (2, 'd'), (3, 'e'), (1, 'o')]
# re-organize data according to new indexing
for index in range(len(data)):
# check if code is shorter than list in current index,
# or the other way around, don't exceed either list
max_substitutions = min(map(len, [code_with_locations, data[index]]))
# create a new list according to new indices
new_list = []
for i in range(max_substitutions):
current_index = code_with_locations[i][0]
new_list.append(data[index][current_index])
# replace old list with new list
data[index] = new_list
print data
Output for 'code' would be:
[['B', 'D', 'E', 'C'],
['D', 'D', 'A', 'E'],
['F', 'D', 'D', 'D'],
['B', 'F', 'E', 'E'],
['D', 'E', 'A', 'A'],
['F', 'B', 'C', 'E']]
With help from itertools
from pprint import pprint
from itertools import chain, izip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
z = ['BC', 'DE', 'DE', 'DA', 'FD', 'DD', 'BE', 'FE', 'DA', 'EA', 'FE', 'BC']
input = 'CODE'
pprint([[b for (a, b) in sorted(zip(input, x))]
for x in grouper(chain.from_iterable(z), len(input))])
[['B', 'D', 'E', 'C'],
['D', 'D', 'A', 'E'],
['F', 'D', 'D', 'D'],
['B', 'F', 'E', 'E'],
['D', 'E', 'A', 'A'],
['F', 'B', 'C', 'E']]

Categories