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
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])
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]
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 a matrix:
matrix = [['F', 'B', 'F', 'A', 'C', 'F'],
['D', 'E', 'B', 'E', 'B', 'E'],
['F', 'A', 'D', 'B', 'F', 'B'],
['B', 'E', 'F', 'B', 'D', 'D']]
I want to remove and collect the first two elements of each sub-list, and add them to a new list.
so far i have got:
while messagecypher:
for vector in messagecypher:
final.extend(vector[:2])
the problem is; the slice doesn't seem to remove the elements, and I end up with a huge list of repeated chars. I could use .pop(0) twice, but that isn't very clean.
NOTE: the reason i remove the elements is becuase i need to keep going over each vector until the matrix is empty
You can keep your slice and do:
final = []
for i in range(len(matrix)):
matrix[i], final = matrix[i][:2], final + matrix[i][2:]
Note that this simultaneously assigns the sliced list back to matrix and adds the sliced-off part to final.
Well you can use a list comprehension to get the thing done, but its perhaps counter-intuitive:
>>> matrix = [['F', 'B', 'F', 'A', 'C', 'F'],
['D', 'E', 'B', 'E', 'B', 'E'],
['F', 'A', 'D', 'B', 'F', 'B'],
['B', 'E', 'F', 'B', 'D', 'D']]
>>> while [] not in matrix: print([i for var in matrix for i in [var.pop(0), var.pop(0)]])
['F', 'B', 'D', 'E', 'F', 'A', 'B', 'E']
['F', 'A', 'B', 'E', 'D', 'B', 'F', 'B']
['C', 'F', 'B', 'E', 'F', 'B', 'D', 'D']
EDIT:
Using range makes the syntax look cleaner:
>>> matrix = [['C', 'B', 'B', 'D', 'F', 'B'], ['D', 'B', 'B', 'A', 'B', 'A'], ['B', 'D', 'E', 'F', 'C', 'B'], ['B', 'A', 'C', 'B', 'E', 'F']]
>>> while [] not in matrix: print([var.pop(0) for var in matrix for i in range(2)])
['C', 'B', 'D', 'B', 'B', 'D', 'B', 'A']
['B', 'D', 'B', 'A', 'E', 'F', 'C', 'B']
['F', 'B', 'B', 'A', 'C', 'B', 'E', 'F']
Deleting elements is not an efficient way to go about your task. It requires Python to perform a lot of unnecessary work shifting things around to fill the holes left by the deleted elements. Instead, just shift your slice over by two places each time through the loop:
final = []
for i in xrange(0, len(messagecypher[0]), 2):
for vector in messagecypher:
final.extend(vector[i:i+2])