I try do enumerate all the group of 2 (possible) in a list of people. Like for example for a group project I went to have all the possibilities of group of two people in the list of the class. (In python).
For example if my list of people is: {a,b,c,d,e,f} I want to have:
I tried a lot of things (itertools.combinations() or itertools.permutations()) but I don't succeed to have this result without tuples or without two times the same person in a group.
You can do the following (copied from https://stackoverflow.com/a/5360442):
lst = ['a','b','c','d','e','f']
def all_pairs(lst):
if len(lst) < 2:
yield []
return
if len(lst) % 2 == 1:
# Handle odd length list
for i in range(len(lst)):
for result in all_pairs(lst[:i] + lst[i+1:]):
yield result
else:
a = lst[0]
for i in range(1,len(lst)):
pair = [a,lst[i]]
for rest in all_pairs(lst[1:i]+lst[i+1:]):
yield [pair] + rest
print(list(all_pairs(lst)))
which gives you:
[[['a', 'b'], ['c', 'd'], ['e', 'f']],
[['a', 'b'], ['c', 'e'], ['d', 'f']],
[['a', 'b'], ['c', 'f'], ['d', 'e']],
[['a', 'c'], ['b', 'd'], ['e', 'f']],
[['a', 'c'], ['b', 'e'], ['d', 'f']],
[['a', 'c'], ['b', 'f'], ['d', 'e']],
[['a', 'd'], ['b', 'c'], ['e', 'f']],
[['a', 'd'], ['b', 'e'], ['c', 'f']],
[['a', 'd'], ['b', 'f'], ['c', 'e']],
[['a', 'e'], ['b', 'c'], ['d', 'f']],
[['a', 'e'], ['b', 'd'], ['c', 'f']],
[['a', 'e'], ['b', 'f'], ['c', 'd']],
[['a', 'f'], ['b', 'c'], ['d', 'e']],
[['a', 'f'], ['b', 'd'], ['c', 'e']],
[['a', 'f'], ['b', 'e'], ['c', 'd']]]
As required.
You can use this built-in function
import itertools
data = ['a', 'b', 'c', 'd', 'e', 'f']
#in case the number of items is odd
len(data) % 2 != 0 and data.append(None)
number_of_groups = int(len(data) / 2)
check = lambda x, y: not list(set(x) & set(y))
test = lambda grps : all([check(x[0], x[1]) for x in itertools.combinations(grps, 2)])
pairs = [list(x) for x in itertools.combinations(['a', 'b', 'c', 'd', 'e', 'f'], 2)]
[list(x) for x in itertools.combinations(pairs, number_of_groups) if test(x)]
The result is [[['a', 'b'], ['c', 'd'], ['e', 'f']], [['a', 'b'], ['c', 'e'], ['d', 'f']], [['a', 'b'], ['c', 'f'], ['d', 'e']], [['a', 'c'], ['b', 'd'], ['e', 'f']], [['a', 'c'], ['b', 'e'], ['d', 'f']], [['a', 'c'], ['b', 'f'], ['d', 'e']], [['a', 'd'], ['b', 'c'], ['e', 'f']], [['a', 'd'], ['b', 'e'], ['c', 'f']], [['a', 'd'], ['b', 'f'], ['c', 'e']], [['a', 'e'], ['b', 'c'], ['d', 'f']], [['a', 'e'], ['b', 'd'], ['c', 'f']], [['a', 'e'], ['b', 'f'], ['c', 'd']], [['a', 'f'], ['b', 'c'], ['d', 'e']], [['a', 'f'], ['b', 'd'], ['c', 'e']], [['a', 'f'], ['b', 'e'], ['c', 'd']]]
import itertools
people = ['a', 'b', 'c', 'd', 'e', 'f']
# number of groups that could be created
n_groups = len(people) // 2
# create all possible pairs
pairs = itertools.combinations(people, 2)
# create all group constellations
group_combo = itertools.combinations(pairs, n_groups)
# check for impossible constellations
# ie. it is not possible to have 'a' in two groups
for group in group_combo:
flatten_group_tuple = [element for tupl in group for element in tupl]
# check for duplicate members, if duplicates exist the set-size will be < n_groups * 2
if len(set(flatten_group_tuple)) == n_groups * 2:
print([list(x) for x in group])
Related
I have a list:
['A', 'B', 'C', ['D', ['E', 'F'], 'G'], 'H']
and I want to turn this into:
[['E', 'F'], ['D', 'G'], ['A', 'B', 'C', 'H']]
So basically I want the sublist on the deepest level of the list to come first in the new list and then counting down the level the remaining sublists.
This should work with any nested list.
If there are two sublists on the same level, then it doesn't really matter which one comes first.
['A', 'B', 'C', ['D', ['E', 'F'], 'G'], ['H', 'I', 'J']]
[['E', 'F'], ['D', 'G'], ['H', 'I', 'J'], ['A', 'B', 'C', 'H']] #this is fine
[['E', 'F'], ['H', 'I', 'J'], ['D', 'G'], ['A', 'B', 'C', 'H']] #this too
I thought of first using a function to determine on what level the deepest sublist is, but then again I don't know how to access items in a list based on their level or if that's even possible.
Been tinkering around this for far too long now and I think my head just gave up, hope someone can assist me with this problem!
You can use a recursive generator function:
def sort_depth(d, c = 0):
r = {0:[], 1:[]}
for i in d:
r[not isinstance(i, list)].append(i)
yield from [i for j in r[0] for i in sort_depth(j, c+1)]
yield (c, r[1])
def result(d):
return [b for _, b in sorted(sort_depth(d), key=lambda x:x[0], reverse=True) if b]
print(result(['A', 'B', 'C', ['D', ['E', 'F'], 'G'], 'H']))
print(result(['A', 'B', 'C', ['D', ['E', 'F'], 'G'], ['H', 'I', 'J']]))
print(result([[1, [2]], [3, [4]]]))
Output:
[['E', 'F'], ['D', 'G'], ['A', 'B', 'C', 'H']]
[['E', 'F'], ['D', 'G'], ['H', 'I', 'J'], ['A', 'B', 'C']]
[[2], [4], [1], [3]]
Here is a relatively straight-forward solution:
def sort_depth(d):
def dlists(obj, dep=0):
for x in filter(list.__instancecheck__, obj):
yield from dlists(x, dep-1)
yield [x for x in obj if not isinstance(x, list)], dep
return [x for x, y in sorted(dlists(d), key=lambda p: p[1])]
>>> [*sort_depth([[1, [2]], [3, [4]]])]
[[2], [4], [1], [3], []]
>>> [*sort_depth(['A', 'B', 'C', ['D', ['E', 'F'], 'G'], 'H'])]
[['E', 'F'], ['D', 'G'], ['A', 'B', 'C', 'H']]
The approach:
Collect all the sublists and annotate them with their (negative) nesting level, e.g. (['E', 'F'], -2)
Sort them by their nesting level
Extract the lists back from the sorted data
Say I have this input data
my_input_list = [[A],[A,B,C],[D],[D,E,F],[A,B,C,D,E,F],[A,C,E]]
items_that_appear_twice = [A,B,C]
items_that_appear_four = [D,E,F]
And I want to create an expansion such that some elements are only allowed to appear twice.
my_output_list = [
[A],[A],
[A,B,C],[A,B,C],
[D],[D],[D],[D],
[D,E,F],[D,E,F],[D,E,F],[D,E,F],
[A,B,C,D,E,F],[A,B,C,D,E,F],[D,E,F],[D,E,F],
[A,C,E],[A,C,E],[E],[E]]
I tired a few ideas and didn't find a really neat solution, like building lists of four and list.remove() from them which generated two empty lists.
For example list removal techniques on my_input_list[0]*4 gives [A],[A],[],[] (two empty lists) when I want [A],[A] .
I have a working version: See Pyfiddle.
my_input_list = [['A'],['A','B','C'],['D'],['D','E','F'],['A','B','C','D','E','F'],['A','C','E']]
items_that_appear_twice = ['A','B','C']
items_that_appear_four = ['D','E','F']
my_output_list = []
for my_input in my_input_list:
items_allowed_to_appear_twice = list(filter(
lambda value: (value in items_that_appear_twice
or value in items_that_appear_four),
my_input))
items_allowed_to_appear_four = list(filter(
lambda value: value in items_that_appear_four,
my_input))
my_output_list += 2*[items_allowed_to_appear_twice]
if len(items_allowed_to_appear_four):
my_output_list += 2*[items_allowed_to_appear_four]
print(my_output_list)
my_input_list = [['A'],
['A', 'B', 'C'],
['D'],
['D', 'E', 'F'],
['A', 'B', 'C', 'D', 'E', 'F'],
['A', 'C', 'E']]
items_that_appear_twice = ['A', 'B', 'C']
items_that_appear_four = ['D', 'E', 'F']
my_output_list = []
for sub in my_input_list:
my_output_list.append(sub)
my_output_list.append(sub)
sub = [x for x in sub if x in items_that_appear_four]
if sub:
my_output_list.append(sub)
my_output_list.append(sub)
assert my_output_list == [
['A'], ['A'],
['A', 'B', 'C'], ['A', 'B', 'C'],
['D'], ['D'], ['D'], ['D'],
['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'],
['A', 'B', 'C', 'D', 'E', 'F'], ['A', 'B', 'C', 'D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'],
['A', 'C', 'E'], ['A', 'C', 'E'], ['E'], ['E']]
Below is my solution, sort the sub list first, then append to the result according different situation.
my_input_list = [['A'],['A','B','C'],['D'],['D','E','F'],['A','B','C','D','E','F'],['A','C','E']]
items_that_appear_twice = ['A','B','C']
items_that_appear_four = ['D','E','F']
result = []
for sublist in my_input_list:
appearence = {1:[], 2:[], 4:[]}
for item in sublist:
appearence[2].append(item) if item in items_that_appear_twice else (appearence[4].append(item) if item in items_that_appear_four else appearence[1].append(item))
if len(appearence[2]) > 0:
result.append([appearence[2] + appearence[4]] * 2 + ([appearence[4]] * 2 if appearence[4] and len(appearence[4]) > 0 else []))
else:
result.append([appearence[4]] * 4)
for item in result:
print(item)
output:
[['A'], ['A']]
[['A', 'B', 'C'], ['A', 'B', 'C']]
[['D'], ['D'], ['D'], ['D']]
[['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F']]
[['A', 'B', 'C', 'D', 'E', 'F'], ['A', 'B', 'C', 'D', 'E', 'F'], ['D', 'E', 'F'], ['D', 'E', 'F']]
[['A', 'C', 'E'], ['A', 'C', 'E'], ['E'], ['E']]
[Finished in 0.178s]
Can some one give me shortcut for this code?
list1 = [['a','b','c'],['d','e','f'],['h'',i','j']]
a = []
i = 0
while i < 4 :
a.append(list1[0])
i += 1
i = 0
while i < 4:
a.append(list1[1])
i += 1
i = 0
while i < 4:
a.append(list1[2])
i += 1
print(a)
I want my output should be:
[['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['h', 'i', 'j'], ['h', 'i', 'j'], ['h', 'i', 'j'], ['h', 'i', 'j']]
A list comprehension would do this for you:
a = [sublist for sublist in list1 for i in range(4)]
Note that this only creates additional references to the lists contained in list1, not copies.
Demo:
>>> list1 = [['a', 'b', 'c'], ['d', 'e', 'f'], ['h,i', 'j']]
>>> a = [sublist for sublist in list1 for i in range(4)]
>>> a
[['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'b', 'c'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['h,i', 'j'], ['h,i', 'j'], ['h,i', 'j'], ['h,i', 'j']]
>>> a[0][0] = 'spam'
>>> a
[['spam', 'b', 'c'], ['spam', 'b', 'c'], ['spam', 'b', 'c'], ['spam', 'b', 'c'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['d', 'e', 'f'], ['h,i', 'j'], ['h,i', 'j'], ['h,i', 'j'], ['h,i', 'j']]
Note how a change to the first nested list is reflected across the first four lists? That's because those are all the same object. If you wanted to create copies, use the list() function, or the [:] identity slice operator:
a = [sublist[:] for sublist in list1 for i in range(4)]
Currently working on a 2D transposition cipher in Python. So I have a list that contains an encoded message, like below:
['BF', 'AF', 'AF', 'DA', 'CD', 'DD', 'BC', 'EF', 'DA', 'AA', 'EF', 'BF']
The next step is taking that list, splitting it up and putting it into a new matrix with regards to a keyword that the user enters. Which I have below:
Enter the keyword for final encryption: hide
H I D E
['B', 'F', 'A', 'F']
['A', 'F', 'D', 'A']
['C', 'D', 'D', 'D']
['B', 'C', 'E', 'F']
['D', 'A', 'A', 'A']
['E', 'F', 'B', 'F']
What I would like to do next and haven't done is take each of the columns above and print them in alphabetical order, therefore getting another cipher text, like below:
D E H I
['A', 'F', 'B', 'F']
['D', 'A', 'A', 'F']
['D', 'D', 'C', 'D']
['E', 'F', 'B', 'C']
['A', 'A', 'D', 'A']
['B', 'F', 'E', 'F']
Here's my code:
def encodeFinalCipher():
matrix2 = []
# Convert keyword to upper case
keywordKey = 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 matrix to the screen
print (' %s' % ' '.join(map(str, keywordKey)))
for letters in matrix2:
print (letters)
return finalEncryption
I have traversed the 2D matrix and got all the column entries like below:
b = [[matrix2[i][j] for i in range(len(matrix2))] for j in range(len(matrix2[0]))]
for index, item in enumerate (b):
print("\n",index, item)
OUTPUT:------
0 ['B', 'A', 'C', 'B', 'D', 'E']
1 ['F', 'F', 'D', 'C', 'A', 'F']
2 ['A', 'D', 'D', 'E', 'A', 'B']
3 ['F', 'A', 'D', 'F', 'A', 'F']
How would I append each letter of the keywordKey (e.g. 'H' 'I' 'D' 'E') to the list where the numbers 0,1,2,3 are?
Or probably a more efficient solution. How would I put the letters into the keywordKey columns when creating the matrix? Would a dictionary help here? Then I could sort the dictionary and print the final cipher.
Many thanks
You can do something like this:
>>> from operator import itemgetter
>>> from pprint import pprint
>>> lst = [['B', 'F', 'A', 'F'],
['A', 'F', 'D', 'A'],
['C', 'D', 'D', 'D'],
['B', 'C', 'E', 'F'],
['D', 'A', 'A', 'A'],
['E', 'F', 'B', 'F']]
>>> key = 'HIDE'
Sort xrange(len(key)) or range(len(key)) using the corresponding values from key and then you will have a list of indices:
>>> indices = sorted(xrange(len(key)), key=key.__getitem__)
>>> indices
[2, 3, 0, 1]
Now all we need to do is loop over the list and apply these indices to each item using operator.itemgetter and get the corresponding items:
>>> pprint([list(itemgetter(*indices)(x)) for x in lst])
[['A', 'F', 'B', 'F'],
['D', 'A', 'A', 'F'],
['D', 'D', 'C', 'D'],
['E', 'F', 'B', 'C'],
['A', 'A', 'D', 'A'],
['B', 'F', 'E', 'F']]
#or simply
>>> pprint([[x[i] for i in indices] for x in lst])
[['A', 'F', 'B', 'F'],
['D', 'A', 'A', 'F'],
['D', 'D', 'C', 'D'],
['E', 'F', 'B', 'C'],
['A', 'A', 'D', 'A'],
['B', 'F', 'E', 'F']]
I have the following code;
for d in range(0,len(newlist),4):
codewordgrid.append(list(codedmessage[d:d+6]))
This then prints something along the lines of this;
<<<CODEWORD GRID>>>
[['E', 'A'], ['E', 'A'], ['E', 'A'], ['F', 'C'], ['F', 'C'], ['F', 'C']]
[['F', 'C'], ['F', 'C'], ['F', 'A'], ['F', 'A'], ['F', 'A'], ['C', 'A']]
[['F', 'A'], ['C', 'A'], ['C', 'A'], ['C', 'A']]
[]
[]
[]
[]
[]
[]
Basically my aim is this - to print the list in an unlimited number of rows (so basically the message can be any length in rows), but to limit the number of columns to 4. So I would like the example to look more like this;
['E', 'A'], ['E', 'A'], ['E', 'A'], ['F', 'C']
['F', 'C'], ['F', 'C'], ['F', 'C'], ['F', 'C']
['F', 'A'], ['F', 'A'], ['F', 'A'], ['C', 'A']
['F', 'A'], ['C', 'A'], ['C', 'A'], ['C', 'A']
So just to reiterate, depending on how long the codedmessage is depends on the amount of rows, however I would like to limit the number of columns to 4 and I am unsure how to do this.
Many thanks.
It seems you have a copy-paste error here, as you just have to change the 6 to 4 when you do the slice. Also note that you use two different lists for the range and for the slice. I think you meant this:
for d in range(0,len(codedmessage),4):
codewordgrid.append(list(codedmessage[d:d+4]))
You can flatten the list with itertools:
chain = itertools.chain.from_iterable(your_nested_list):
for i in range(0, len(chain), 4):
print str(chain[i:i+4])[1:-1]
Or without itertools or other modules:
def eachToken(codewordgrid):
for element in codewordgrid:
for token in element:
yield token
for i, token in enumerate(eachToken(codewordgrid)):
print token, ("," if i % 4 else "\n"),
print
Numpy should be able to handle this pretty easily. If codedmessage is a list:
import numpy
codewordgrid = numpy.array(codedmessage).reshape(-1,4,2)
numpy.savetxt('codewordgrid.txt',codewordgrid,fmt='%s',delimiter=", ")
The only difference here is that there won't be a comma between codeword pairs.
e.g.
['E' 'A'], ['E' 'A'], ['E' 'A'], ['F' 'C']
['F' 'C'], ['F' 'C'], ['F' 'C'], ['F' 'C']
['F' 'A'], ['F' 'A'], ['F' 'A'], ['C' 'A']
['F' 'A'], ['C' 'A'], ['C' 'A'], ['C' 'A']