Python - itertools.product without using element more than once - python

I have a list of lists, and I would like to duplicate the effect of itertools.product() without using any element more than once.
>>> list = [['A', 'B'], ['C', 'D'], ['A', 'B']]
>>> [''.join(e) for e in itertools.product(*list)]
['ACA', 'ACB', 'ADA', 'ADB', 'BCA', 'BCB', 'BDA', 'BDB']
>>> # Desired output: ['ACB', 'ADB', 'BCA', 'BDA']
The list I need to use this on is too large to compute itertools.product and remove the unneeded elements. (25 billion permutations from itertools.product, while my desired output only has ~500,000). Preferably, an answer will be iterable.
Edit: I know "product" is the wrong term for what I need, but I'm struggling to find the word I'm looking for.
Edit2: This is the list that I desire to perform this operation on:
[['A', 'H', 'L'], ['X', 'B', 'I'], ['Q', 'C', 'V'], ['D', 'N'], ['E', 'F'], ['E', 'F'], ['G'], ['A', 'H', 'L'], ['X', 'B', 'I'], ['W', 'U', 'J', 'K', 'M'], ['W', 'U', 'J', 'K', 'M'], ['A', 'H', 'L'], ['W', 'U', 'J', 'K', 'M'], ['D', 'N'], ['P', 'O', 'T'], ['P', 'O', 'T'], ['Q', 'C', 'V'], ['R'], ['S'], ['P', 'O', 'T'], ['W', 'U', 'J', 'K', 'M'], ['Q', 'C', 'V'], ['W', 'U', 'J', 'K', 'M'], ['X', 'B', 'I']]

A simple stack-based implementation:
def product1(l): return product1_(l,0,[])
def product1_(l,i,buf):
if i==len(l): yield buf
else:
for x in l[i]:
if x not in buf:
buf.append(x)
yield from product1_(l,i+1,buf)
buf.pop()
This is a bit slower than Patrick Haugh's answer (I get 18 s for your test case), but it gives the results in a predictable order.
Note that you have to process the values as it generates "them", since they're all the same list buf; you could write yield tuple(buf) or yield "".join(buf) to generate separate "cooked" values (at a cost of less than one additional second).
If the values are letters, you could use a "bitmask" list to implement the collision test, which reduces the time to about 13 s (but using a set is just as fast). Other possible optimizations include processing lists with fewer eligible elements first, to reduce backtracking; this can get this case down to 11 s.

test1 = [['A', 'B'], ['C', 'D'], ['A', 'B']]
test2 = [['A', 'H', 'L'], ['X', 'B', 'I'], ['Q', 'C', 'V'], ['D', 'N'], ['E', 'F'], ['E', 'F'], ['G'], ['A', 'H', 'L'],
['X', 'B', 'I'], ['W', 'U', 'J', 'K', 'M'], ['W', 'U', 'J', 'K', 'M'], ['A', 'H', 'L'],
['W', 'U', 'J', 'K', 'M'], ['D', 'N'], ['P', 'O', 'T'], ['P', 'O', 'T'], ['Q', 'C', 'V'], ['R'], ['S'],
['P', 'O', 'T'], ['W', 'U', 'J', 'K', 'M'], ['Q', 'C', 'V'], ['W', 'U', 'J', 'K', 'M'], ['X', 'B', 'I']]
def prod(curr, *others):
if not others:
for x in curr:
yield {x} # (x,) for tuples
else:
for o in prod(*others):
for c in curr:
if c not in o:
yield {c, *o} # (c, *o) for tuples
print([''.join(x) for x in prod(*test1)])
# ['ABC', 'ABD', 'ABC', 'ABD']
print(sum(1 for x in prod(*test2)))
# 622080
The longer input takes about five seconds to run on my machine. I use sets to pass values around because they are much more efficent than tuples or lists when it comes to membership checks. If necessary, you can use tuples, it will just be slower.
Some questions to think about: does order matter? What do you want to happen when we can't use an item from the current list (because they've all already been used)?

Your specific case has an interesting property. If we arrange it in a counter, we see that every list occurs as many times as its entries:
Counter({('A', 'H', 'L'): 3,
('D', 'N'): 2,
('E', 'F'): 2,
('G',): 1,
('P', 'O', 'T'): 3,
('Q', 'C', 'V'): 3,
('R',): 1,
('S',): 1,
('W', 'U', 'J', 'K', 'M'): 5,
('X', 'B', 'I'): 3})
In other words, ignoring order, the sequences you want are the cartesian products of permutations of your lists. Suppose your list is called l. Then we can assemble a list of all the permutations of the sublists and take their product:
c = set(tuple(i) for i in l)
permutations = [list(itertools.permutations(i)) for i in c]
permutation_products = itertools.products(*permutations)
An element of permutation_products looks something like:
(('W', 'U', 'J', 'K', 'M'),
('E', 'F'),
('X', 'B', 'I'),
('Q', 'C', 'V'),
('P', 'O', 'T'),
('D', 'N'),
('G',),
('S',),
('R',),
('A', 'L', 'H'))
We have to get it back into the right order. Suppose our permutation is called perm. For each sublist of your list, we have to locate the correct element of perm and then take the next letter in the permutation. We can do this by making a dictionary:
perm_dict = {frozenset(p): list(p) for p in perm}
Then, to construct a single permutation, we have:
s = "".join([perm_dict[frozenset(i)].pop() for i in l])
We can combine all this into a generator:
def permute_list(l):
c = set(tuple(i) for i in l)
permutations = [list(itertools.permutations(i)) for i in c]
permutation_products = itertools.product(*permutations)
for perm in permutation_products:
perm_dict = {frozenset(p): list(p) for p in perm}
yield "".join([perm_dict[frozenset(i)].pop() for i in l])

Related

To find commonality/characteristics of in a group of lists

A group of lists of equal length (strings as elements). I want to find out their commonality/characteristics.
Let’s call them “good” lists - I want to find out what makes a “good” list.
A thinking is to output all 3-element combination from each list, then summarize the occurrences of the 3-element to rank them.
For example, “D” and “N” and “T” appeared 4 times. It may conclude that, when “D” and “N” and “T” appear in a list, it is a “good” list.
The same method can apply to 4-element combinations, 5-element combinations (when the many lists are very long).
What would be a better solution?
import itertools
from itertools import combinations
from collections import Counter
s = [
['O', 'V', 'R', 'M', 'Y'],
['I', 'Q', 'L', 'J', 'A'],
['M', 'I', 'Q', 'N', 'G'],
['Y', 'M', 'R', 'Q', 'Z'],
['D', 'X', 'C', 'Q', 'N'],
['B', 'O', 'Q', 'E', 'V'],
['V', 'M', 'J', 'G', 'R'],
['M', 'T', 'L', 'I', 'Z'],
['Y', 'H', 'A', 'V', 'L'],
['O', 'T', 'D', 'N', 'E'],
['D', 'N', 'T', 'I', 'G'],
['T', 'Q', 'H', 'I', 'P'],
['F', 'T', 'D', 'W', 'N'],
['F', 'Z', 'H', 'E', 'X'],
['E', 'Z', 'R', 'K', 'J'],
['P', 'C', 'U', 'D', 'F'],
['N', 'I', 'Y', 'U', 'E'],
['T', 'N', 'D', 'L', 'V'],
['D', 'Z', 'I', 'P', 'X'],
['H', 'L', 'C', 'P', 'Y']]
summary = []
for each in s:
all_combinations = [comb for comb in combinations(each, 3)] # unique combinations only
for a in all_combinations:
summary.append('-'.join(sorted(a)))
print (Counter(summary))
Output:
Counter({'D-N-T': 4, 'M-R-V': 2, 'M-R-Y': 2.....})
I think I would make use of an sql database to do this (sqlite in the examples below). You could create a table with two columns element and list (both primary keys). You would populate it with the elements from all lists like this:
element | list
-----------------
O 1
V 1
R 1
M 1
Y 1
I 2
Q 2
L 2
J 2
A 2
...
You could run a query like the following to figure out which elements appear how many times in the lists:
SELECT element, count(element) as cnt FROM table, (SELECT distinct(element) as dst from table) as table2
WHERE element = table2.dst
GROUP BY element
ORDER BY cnt DESC
You can also run a query to find all lists that contain your elements of choice. Here is the query to find all lists that contain D, N and T:
SELECT COUNT(element), list FROM table WHERE element IN ('D', 'N', 'T')
GROUP BY list
HAVING COUNT(element) = 3;

Creating minimal groups from two lists

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']]

Split lists into chunk based of index of another list

I want to split a list into chunks using values of of another list as the range to split.
indices = [3, 5, 9, 13, 18]
my_list = ['a', 'b', 'c', ..., 'x', 'y', 'z']
So basically, split my_list from range:
my_list[:3], mylist[3:5], my_list[5:9], my_list[9:13], my_list[13:18], my_list[18:]
I have tried to indices into chunks of 2 but the result is not what i need.
[indices[i:i + 2] for i in range(0, len(indices), 2)]
My actual list length is 1000.
You could also do it using simple python.
Data
indices = [3, 5, 9, 13, 18]
my_list = list('abcdefghijklmnopqrstuvwxyz')
Solution
Use list comprehension.
[(my_list+[''])[slice(ix,iy)] for ix, iy in zip([0]+indices, indices+[-1])]
Output
[['a', 'b', 'c'],
['d', 'e'],
['f', 'g', 'h', 'i'],
['j', 'k', 'l', 'm'],
['n', 'o', 'p', 'q', 'r'],
['s', 't', 'u', 'v', 'w', 'x', 'y', 'z']]
Check if correct order of indices are extracted
dict(((ix,iy), (my_list+[''])[slice(ix,iy)]) for ix, iy in zip([0]+indices, indices+[-1]))
Output
{(0, 3): ['a', 'b', 'c'],
(3, 5): ['d', 'e'],
(5, 9): ['f', 'g', 'h', 'i'],
(9, 13): ['j', 'k', 'l', 'm'],
(13, 18): ['n', 'o', 'p', 'q', 'r'],
(18, -1): ['s', 't', 'u', 'v', 'w', 'x', 'y', 'z']}
Can use itertools.zip_longest
[my_list[a:b] for a,b in it.zip_longest([0]+indices, indices)]
[['a', 'b', 'c'],
['d', 'e'],
['f', 'g', 'h', 'i'],
['j', 'k', 'l', 'm'],
['n', 'o', 'p', 'q', 'r'],
['s', 't', 'u', 'v', 'x', 'y', 'z']]
A little bit of code golf for fun:
map(my_list.__getitem__, map(lambda s: slice(*s), it.zip_longest([0]+indices, indices)))
One way using itertools.tee and pairwise:
from itertools import tee
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
chunks = [my_list[i:j] for i, j in pairwise([0, *indices, len(my_list)])]
print(chunks)
Output:
[['a', 'b', 'c'],
['d', 'e'],
['f', 'g', 'h', 'i'],
['j', 'k', 'l', 'm'],
['n', 'o', 'p', 'q', 'r'],
['s', 't', 'u', 'v', 'w', 'x', 'y', 'z']]
If numpy is an option, use numpy.array_split, which is meant for this:
import numpy as np
np.array_split(my_list, indices)
Output:
[array(['a', 'b', 'c'], dtype='<U1'),
array(['d', 'e'], dtype='<U1'),
array(['f', 'g', 'h', 'i'], dtype='<U1'),
array(['j', 'k', 'l', 'm'], dtype='<U1'),
array(['n', 'o', 'p', 'q', 'r'], dtype='<U1'),
array(['s', 't', 'u', 'v', 'w', 'x', 'y', 'z'], dtype='<U1')]

Sort a list by a key and sort another list in the same way as the first?

For example there are three lists:
unsorted_key = ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
sorted_key = ['e', 'i', 'o', 'p', 'q', 'r', 't', 'u', 'w', 'y']
ciphertext = [
['u', 't', 'x', 'e'],
['p', 'r', 'k', 'p'],
['v', 'n', 'x', 'a'],
['n', 'h', 'e', 'x'],
['x', 'h', 'm', 's'],
['l', 'x', 'c', 'x'],
['x', 'c', 'y', 'a'],
['t', 'u', 'o', 'x'],
['e', 'r', 'm', 'e'],
['y', 'y', 'e', 'x']
]
Is it possible to take the order of the sorted_key and sort it into the unsorted_key, and take the order of the ciphertext and sort it in an identical way?
When moving 'q' from sorted_key[4] to sorted_key[0], it should move ciphertext[4] to ciphertext[0].
All three lists will always be of equal length.
The sorted_key and unsorted_key will never have repeating elements.
The sorted_key will always be a sorted version of unsorted_key.
I've been thinking about it and the only way I can think of would be to use a helper function to dynamically generate and return a lambda function from the order of unsorted_key, and then use something like:
sorted_key, ciphertext = (list(i) for i in zip(*sorted(zip(sorted_key, ciphertext), key=generate(unsorted_key))))
But I really don't know how zip() or lambda functions work or how to make a custom sorting order into one, or if one can even be returned to be used in sorted(). I really can't seem to wrap my head around this problem, so any help would be greatly appreciated!
An efficient approach to solve this problem in linear time is to create a dict that maps keys to indices of sorted_key, and then create a mappping dict that maps indices of unsorted_key to indices of sorted_key based on the same keys, so that you can iterate an index through the range of length of ciphertext to generate a list in the mapped order:
order = dict(map(reversed, enumerate(sorted_key)))
mapping = {i: order[k] for i, k in enumerate(unsorted_key)}
print([ciphertext[mapping[i]] for i in range(len(ciphertext))])
This outputs:
[['x', 'h', 'm', 's'], ['e', 'r', 'm', 'e'], ['u', 't', 'x', 'e'], ['l', 'x', 'c', 'x'], ['x', 'c', 'y', 'a'], ['y', 'y', 'e', 'x'], ['t', 'u', 'o', 'x'], ['p', 'r', 'k', 'p'], ['v', 'n', 'x', 'a'], ['n', 'h', 'e', 'x']]
The builtin sorted with a custom key can do it for you:
sorted(ciphertext, key=lambda x: unsorted_key.index(sorted_key[ciphertext.index(x)]))
Output:
[['x', 'h', 'm', 's'],
['e', 'r', 'm', 'e'],
['u', 't', 'x', 'e'],
['l', 'x', 'c', 'x'],
['x', 'c', 'y', 'a'],
['y', 'y', 'e', 'x'],
['t', 'u', 'o', 'x'],
['p', 'r', 'k', 'p'],
['v', 'n', 'x', 'a'],
['n', 'h', 'e', 'x']]
The lambda basically boils down to:
Find the current index
Find the value of current index in sorted_key
Find the index of sorted_key value in unsorted_key
Sort it
The one thing that I'm not clear about is why do you need to "sort" sorted_key if the end result is identical to unsorted_key? Just sorted_key = unsorted_key[:] is simple enough if that's the case. But if you really need to sort sorted_key as well, you can do this (it would actually make the lambda simpler):
ciphertext, sorted_key = map(list, zip(*sorted(zip(ciphertext, sorted_key), key=lambda x: unsorted_key.index(x[1]))))
ciphertext
[['x', 'h', 'm', 's'],
['e', 'r', 'm', 'e'],
['u', 't', 'x', 'e'],
['l', 'x', 'c', 'x'],
['x', 'c', 'y', 'a'],
['y', 'y', 'e', 'x'],
['t', 'u', 'o', 'x'],
['p', 'r', 'k', 'p'],
['v', 'n', 'x', 'a'],
['n', 'h', 'e', 'x']]
sorted_key
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
I'm not sure I get the point, but...
First determine the moves (can be the opposite, it0s not clear to me):
moves = [ [i, sorted_key.index(c)] for i, c in enumerate(unsorted_key) ]
#=> [[0, 4], [1, 8], [2, 0], [3, 5], [4, 6], [5, 9], [6, 7], [7, 1], [8, 2], [9, 3]]
Maybe swap elements in [i, sorted_key.index(c)].
Apply the moves to a receiver (res):
res = [ None for _ in range(len(ciphertext))]
for a, b in moves:
res[a] = ciphertext[b]
So the output should be:
for line in res:
print(line)
# ['x', 'h', 'm', 's']
# ['e', 'r', 'm', 'e']
# ['u', 't', 'x', 'e']
# ['l', 'x', 'c', 'x']
# ['x', 'c', 'y', 'a']
# ['y', 'y', 'e', 'x']
# ['t', 'u', 'o', 'x']
# ['p', 'r', 'k', 'p']
# ['v', 'n', 'x', 'a']
# ['n', 'h', 'e', 'x']
For testing execution time
import timeit, functools
def custom_sort(ciphertext, sorted_key, unsorted_key):
return [ ciphertext[b] for _, b in [ [i, sorted_key.index(c)] for i, c in enumerate(unsorted_key) ] ]
custom_sort = timeit.Timer(functools.partial(custom_sort, ciphertext, sorted_key, unsorted_key))
print(custom_sort.timeit(20000))
I'm not sure I'm understanding your question properly, but if you're attempting to sort the unsorted key, and ensure that the ciphertexts are sorted accordingly, this should do what you want:
pairs = zip(unsorted_key, ciphertext)
sorted_key = []
sorted_ciphertexts = []
for t in sorted(pairs):
sorted_key.append(t[0])
sorted_ciphertexts.append(t[1])
I'm sure there's probably a more elegant way to do it, but this will ensure that the key and ciphertexts are placed at the same index.

Python: Iterating through the columns in a list of list to find the palindromes

Here I have a word list as:
[['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
And I have to display all the palindromes in this list which are in rows as well as columns.
I have coded to find all the palindromes in the rows. But cannot implement a method to find the palindromes in the columns.
Here is my code so far:
result_1=""
if len(palindrome)==len_line_str:
for row in range(len(palindrome)):
for horizontal_line in range(len(palindrome[row])):
if ''.join(palindrome[row])==''.join(reversed(palindrome[row])):
result_1=''.join(palindrome[row])+" is a palindrome starting at ["+str(row)+"]["+str(row)+"] and is a row in the table"
print(result_1)
Which will display the output:
rotor is a palindrome starting at [0][0] and is a row in the table
Where "rotor" is a palindrome.
I need a method to get the palindromes in the columns which are:
"refer", "tenet", "radar"
Any help is much appreciated. Thanks in advance!
You can use zip to transpose your lists:
>>> t = [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
[['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
>>> list(zip(*t))
[('r', 'e', 'f', 'e', 'r'), ('o', 'v', 'i', 'n', 'a'), ('t', 'e', 'n', 'e', 't'), ('o', 'i', 'e', 't', 'e'), ('r', 'a', 'd', 'a', 'r')]
Your columns are now rows, and you can apply the same method than before. If you just need the words, you can use list comprehensions:
>>> rows = [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
>>> [''.join(row) for row in rows if row[::-1] == row ]
['rotor']
>>> [''.join(column) for column in zip(*rows) if column[::-1] == column ]
['refer', 'tenet', 'radar']
This will do the job:
palindrome=[['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
n=len(palindrome)
for col in range(len(palindrome[0])):
col_word=[palindrome[i][col] for i in range(n)]
if ''.join(col_word)==''.join(reversed(col_word)):
result=''.join(col_word)+" is a palindrome starting at ["+str(col)+"] and is a col in the table"
print(result)
This prints
refer is a palindrome starting at [0] and is a col in the table
tenet is a palindrome starting at [2] and is a col in the table
radar is a palindrome starting at [4] and is a col in the table
Basically, in order to access the words in the column, you can do
col_word=[palindrome[i][col] for i in range(n)]
This fixes the column and iterates over the rows. The rest of the code is structures similarly to yours.
​
I saw you did not want to use Zip (which I would recommend using):
Alternative answer:
list_ = [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
You can get the palindromes (rows) by checking each list with the reversed list [::-1]:
[i==i[::-1] for i in list_]
# prints [True, False, False, False, False]
And get the palindromes (columns) by 1. create the column list (called list_2 below) with a list comprehension and 2. same principle as above:
list_2 = [[i[ind] for i in list_] for ind in range(len(list_))]
[i==i[::-1] for i in list_2]
# prints [True, False, True, False, True]
Update
If you want the answers directly you can do:
[i for i in list_ if i==i[::-1]]
# prints [['r', 'o', 't', 'o', 'r']]
# and list_2: [['r', 'e', 'f', 'e', 'r'],['t', 'e', 'n', 'e', 't'],['r', 'a', 'd', 'a', 'r']]
There are a lot of ways to do it. I will take as example your code because of your effort on it
Another alternative following your code, is creating the columns in another list and check wich of them are palindromes:
palindrome = [['r', 'o', 't', 'o', 'r'],
['e', 'v', 'e', 'i', 'a'],
['f', 'i', 'n', 'e', 'd'],
['e', 'n', 'e', 't', 'a'],
['r', 'a', 't', 'e', 'r']]
len_line_str = 5
result_1=""
def is_pal(string):
return string == reversed(string)
colums = []
if len(palindrome)==len_line_str:
for row in range(len(palindrome)):
vertical = []
if ''.join(palindrome[row])==''.join(reversed(palindrome[row])):
result_1+=''.join(palindrome[row])+" is a palindrome starting at ["+str(0)+"]["+str(row)+"] and is a row in the table. " + "\n"
for horizontal_line in range(len(palindrome[row])):
if(len_line_str-1 > horizontal_line):
vertical += [palindrome[horizontal_line][row]]
else:
vertical += [palindrome[horizontal_line][row]]
colums += [(vertical,row)]
for word in colums:
if ''.join(word[0])==''.join(reversed(word[0])):
result_1+=''.join(word[0])+" is a palindrome starting at ["+str(0)+"]["+str(word[1])+"] and is a column in the table" + "\n"
print(result_1)
This should work. First loop iterates through the list s and the second loop iterates through each list.
Assuming s is the name of the list- [['r', 'o', 't', 'o', 'r'], ['e', 'v', 'e', 'i', 'a'], ['f', 'i', 'n', 'e', 'd'], ['e', 'n', 'e', 't', 'a'], ['r', 'a', 't', 'e', 'r']]
for i in xrange(0,len(s),1):
str = ""
for j in s:
str = str + j[i]
print str
if str == str[::-1]:
print str," is a pallindrome - column", i
else:
print str," is not a pallindrome - column", i
There is no column wise traversal in Python. One hacky way you can follow is to perform transpose operation on your input matrix. Below is a simple way to implement transpose using list comprehensions.
def transpose(matrix):
if not matrix:
return []
return [[row[i] for row in matrix] for i in range(len(matrix[0]))]
Your same logic should work once modify your input using transpose.
Hope this helps!!

Categories