Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Problem
I have to get elements from a text file to a list, diagonally, and from top to buttom. It should work on any dimension of the letters.txt. The file would look like this:
Text file: letters.txt (thought it would be hard, I removed 'Y', and 'Z' from my original post
A B C D E F
G H I J K L
M N O P Q R
S T U V W X
the lists should look like this:
topButtom_List = ['AGMS', 'BHNT', 'CIOU', 'DJPV', 'EKQW', 'FLRX']
bLeftToURight = ['A', 'GB', 'MHC', 'SNID', 'TOJE', 'UPKF', 'VQL', 'WR', 'X']
My current code for top to buttom:
# top to buttom
topButtom_List = [] #should be ['AGMS', 'BHNT', 'CIOU', 'DJPV', 'EKQW', 'FLRX']
openFile = open("letters.txt")
for i in openFile:
i = i.replace(" ","")
length = len(i)
openFile.close()
openFile = open("letters.txt")
counter = 0
for eachIterration in range(length):
for line in openFile:
line = line.replace(" ","")
# counter should be added by 1 each time inner loop itterates x4, and outter loop x1.
topButtom_List.append(line[counter])
counter = counter + 1
openFile.close()
What I was trying to do with the code above:
I was trying to get the top to buttom characters from the text file and get it in a list called topButtom_List. I used counter to define the index that for every iteration the outer loop does, the index would be added by 1. The way I see it is, the outerloop will start, the inner loop will iterate x4 adding AGMS in the topButtom_List on the first iteration, the outer loop will iterate again and add 1 to counter. BHNTZ will be added on the second iteration and so on, the outer loop will iterate again and add 1 to counter.
From the text file: letters.txt
I want to populate topButtom_List
Output I am getting:
['A', 'G', 'M', 'S']
Expected output:
['AGMS', 'BHNT', 'CIOU', 'DJPV', 'EKQW', 'FLRX']
#!/usr/bin/python3
field = """A B C D E F
G H I J K L
M N O P Q R
S T U V W X"""
arr = [col.split(' ') for col in [row.strip() for row in field.split('\n')]]
len_x, len_y = len(arr[0]), len(arr)
len_all = len_x + len_y - 1
lines, groups = [], []
for i in range(len_all):
start = (i, 0) if i < len_y else (len_y-1, i-len_y+1)
end = (0, i) if i < len_x else (i-len_x+1, len_x-1)
lines.append([start, end])
print('List of start and end points: ', lines)
for start, end in lines:
group = ''
for i in range(len_x):
y, x = start[0] - i, start[1] + i
if y >= 0 and y < len(arr) and x < len(arr[y]):
group += arr[y][x]
else:
groups.append(group)
break
print(groups)
Returns
List of start and end points: [[(0, 0), (0, 0)], [(1, 0), (0, 1)],
[(2, 0), (0, 2)], [(3, 0), (0, 3)], [(3, 1), (0, 4)], [(3, 2), (0, 5)],
[(3, 3), (1, 5)], [(3, 4), (2, 5)], [(3, 5), (3, 5)]]
and
['A', 'GB', 'MHC', 'SNID', 'TOJE', 'UPKF', 'VQL', 'WR', 'X']
Related
I want to count the 0s between two 1s from a list.
For example:
l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]
I want the output to be [4,2,1]. How can I do that in python?
A slightly different way using itertools.groupby - using the fact that any entries beyond the first and last 1 is irrelevant to us
from itertools import groupby
first_one = l.index(1) # index of the first "1"
last_one = len(l) - l[::-1].index(1) - 1 # index of the last "1"
out = [len(list(g)) for k, g in groupby(l[first_one:last_one], key=lambda x: x == 0) if k]
Output
[4, 2, 1]
My one-liner
Just for fun (not that I encourage doing so in real project), here is a one liner (but a big line), using almost all iterators in itertools (well, not nearly, in reality. There are really lot of them)
(y for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))) if x==0)
It's an iterator, and nowhere in the process do I build any list. So it would work f l was itself an iterator giving billions of 1 and 0, without using any memory
Explanation
itertools.groupby(l)
is an iterator giving subiterators for each new value of l.
So
for v,it in itertools.groupby(l):
for x in it:
print(x)
Just prints all elements of l. But with 9 iterations for x in it, one in which x is 1 time 0, then one in which x is 1 time 1, then one in which x is 4 times 0, then etc.
If y is an iterator, then sum(1 for _ in y) is the number of iterations.
So
((x,sum(1 for _ in y)) for x,y in itertools.groupby(l))
iterates pairs (value0or1, numberOfSuchInGroup), with alternating value0or1
For example
list((x,sum(1 for _ in y)) for x,y in itertools.groupby(l))
here is
[(0, 1), (1, 1), (0, 4), (1, 3), (0, 2), (1, 1), (0, 1), (1, 2), (0, 1)]
If want to drop the first pair, at least if it is a group of 0, since leading 0 does not count. Plus, I want to play with another iterator, which is dropwhile. So
itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))
is the same iterator as before. But without the first pair if it is group of 0
list(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l))))
is
[(1, 1), (0, 4), (1, 3), (0, 2), (1, 1), (0, 1), (1, 2), (0, 1)]
I also want do drop the last pair (at least if it is a group of 0, but it doesn't hurt if I drop it also if it is a group of 1). And I haven't played with pairwise yet. Which iterates through pairs of subsequent elemnents
list(itertools.pairwise(range(5)))
is
((0,1),(1,2),(2,3),(3,4))
for example
Here, I use it for a very silly reason: just to drop the last item, since of course, there is one less item in pairwise iteration. In my last example, we have 4 items
list(x for x,y in itertools.pairwise(range(5)))
is
[0,1,2,3]
So, strange usage of pairwise, but it drops the last iteration used that way.
So in our case
((x,y) for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))))
is the same iterator as before, but without the last pair
list((x,y) for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))))
is
[(1, 1), (0, 4), (1, 3), (0, 2), (1, 1), (0, 1), (1, 2)]
Now that we have only groups of 0 that are valid, we can filter out the 1s
list((x,y) for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))) if x==0)
is
[(0, 4), (0, 2), (0, 1)]
Plus, we don't need the 0s because at this stage they are all 0 anyway.
So, keep just y not (x,y)
list(y for (x,y),(z,t) in itertools.pairwise(itertools.dropwhile(lambda x: x[0]==0, ((x,sum(1 for _ in y)) for x,y in itertools.groupby(l)))) if x==0)
Is
[4,2,1]
One option using pandas:
l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]
s = pd.Series(l)
out = (s[s.eq(0)&s.cummax()&s.loc[::-1].cummax()]
.rsub(1).groupby(s.ne(0).cumsum()).sum()
.tolist()
)
With pure python and itertools.groupby:
from itertools import groupby
l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]
out = []
start = False
for k, g in groupby(l):
if k == 1:
if start:
out.append(count)
start = True
else:
count = len(list(g))
output: [4, 2, 1]
An old-school answer.
def count(l: list[int]) -> list[int]:
res = []
counter = 0
lastx = l[0]
for x in l[1:]:
rising = (x-lastx) > 0
if rising and counter != 0:
res.append(counter)
counter = counter+1 if x==0 else 0
lastx = x
return res
count([0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]) # [4, 2, 1]
How about this, explanation is all in the code:
l = [0,1,0,0,0,0,1,1,1,0,0,1,0,1,1,0]
output = []
for i in range(len(l)): #this goes through every index in l (1,2,3,...15, 16)
if l[i] == 1: #if in the list at the current index is a 1 it
zeros = 0 #sets zeros to 0
while l[i+1+zeros] == 0: #and starts a while loop as long as the current index+1+'the amount of zeros after the last number 1' is a zero. (so it stops when it reaches another 1)
zeros += 1 # because the while loop still runs it adds another 0
if i+1+zeros == len(l): #the current index + 1 + 'the amount of zeros' = the length of our list
zeros = 0 # it sets zeros back to 0 so the program doesn't add them to the output (else the output would be [4, 2, 1, 1])
break #breaks out of the loop
if zeros > 0: #if the zeros counted between two 1s are more then 0:
output.append(zeros) # it adds them to our final output
print(output) #prints [4, 2, 1] to the terminal
I create a boogle game, and I need to build a function that receives in input: the letter board (list of lists), the list of legal words and an integer n.
The function must return all n-length tracks of valid words.
For example n = 3 then the function must return all the paths on the three-length board which are actually valid words.
I wrote a code that returns in a particular example one route out of three routes that must be returned.
Input:
board1 = [['Q', 'O', 'Q', 'Q'],
['D', 'O', 'G', 'Q'],
['Q', 'O', 'Q', 'Q'],
['Q', 'Q', 'Q', 'Q']]
word_dict = {'DOG': True}
n = 3
board = Board(board1)
length_n_paths(3, board, word_dict)
My Output:
[((1, 0), (1, 1), (1, 2))]
Wanted Output:
[[(1, 0), (0, 1), (1, 2)], [(1, 0), (1, 1), (1, 2)], [(1, 0), (2, 1), (1, 2)]]
I used a combination, first I found all the possible combinations of letters of length n, then I went through a coordinate coordinate and checked if each coordinate is in a valid position according to the coordinate in front of it, and then I checked if the word coming out of the letter combination is a word from the word list.
If so - I will return its path in a list with the other legal words paths.
my code:
direct_lst=['Up','Down','Right','Left','Up_right','Up_left','Down_right','Down_left']
class Board:
def __init__(self, board):
self.board = board
def get_board_coordinate(self):
cord_lst = []
row = len(self.board)
col = len(self.board[0])
for i in range(row):
for j in range(col):
cord_lst.append((i, j))
return cord_lst
def possible_directions(self, coordinate, next_coordinate):
y, x = coordinate
directions_funcs = {
# A dictionary that matches between a letter and the direction of the desired search
'Up': (y - 1, x),
'Down': (y + 1, x),
'Right': (y, x + 1),
'Left': (y, x - 1),
'Up_right': (y - 1, x + 1),
'Up_left': (y - 1, x - 1),
'Down_right': (y + 1, x + 1),
'Down_left': (y + 1, x + 1)
}
it_ok = False
for direction in direct_lst:
if directions_funcs[direction] == next_coordinate:
it_ok = True
return it_ok
def is_valid_path(board, path, words):
word = board.board[path[0][0]][path[0][1]]
board_coordinates = board.get_board_coordinate()
for cord in range(len(path)-1):
if path[cord] in board_coordinates and path[cord+1] in board_coordinates:
if not board.possible_directions(path[cord], path[cord + 1]):
return None
else:
word += board.board[path[cord + 1][0]][path[cord + 1][1]]
else:
return None
if word in set(words):
return word
import itertools
def create_dict(board, n):
new_dict = dict()
row = len(board.board)
col = len(board.board[0])
for i in range(row):
for j in range(col):
new_dict[(i, j)] = board.board[i][j]
result_list = list(map(list, itertools.combinations(new_dict.items(), n)))
return result_list
def coordinates_lst_and_str_lst(board, n):
combine = create_dict(board, n)
all_cord_dic = dict()
for lst in combine:
is_it_ok = True
cord_lst = []
str_l = ""
for i in range(n):
cord_lst.append(lst[i][0])
str_l += lst[i][1]
try:
if not board.possible_directions(lst[i][0], lst[i + 1][0]):
is_it_ok = False
break
except IndexError:
break
if is_it_ok:
all_cord_dic[tuple(cord_lst)] = str_l
all_cord_dic[tuple(cord_lst)[::-1]] = str_l[::-1]
return all_cord_dic
def length_n_paths(n, board, words):
possible_words = coordinates_lst_and_str_lst(board, n)
my_dict = {key:val for key, val in possible_words.items() if val in words}
return list(my_dict.keys())
I think the problem is in the combination but I dont know how to fix it.
I would be happy for any help.
After debugging, it's apparent that the result possible_words does not contain the key (1, 0), (0, 1), (1, 2), so that explains why it's not part of the answer - so the question becomes why doesn't the call to coordinates_lst_and_str_lst() generate that tuple (and the other 'missing' one)
If you break after constructing combine in coordinates_lst_and_str_lst, you will find that [((1, 0), 'D'), ((0, 1), 'O'), ((1, 2), 'G')] is not in combine, this means coordinates_lst_and_str_lst can't find it as a solution.
So, the problem must be in create_dict, which apparently isn't creating all the legal moves.
And indeed, in create_dict, you use itertools.combinations(), which gives you all the unique combinations of n items from a collection, disregarding their order, but you care about the order.
So, you don't want itertools.combinations(new_dict.items(), n), you want itertools.permutations(new_dict.items(), n). Have a closer look at the difference between combinations and permutations (of size n).
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have an array that contains strings, like this:
l = ["abc",
"def",
"hij",
"klm",
"nop",
"qrs"]
and another one with words:
word = ["abc","knq","knop"]
What I need to find the word in the list and return the respecting coordinates.
The particularity is that the searching must be in horizontal or vertical or together.
For examples the first word:
abc return the index [(0,0)(0,1)(0,2)]
knq return [(3,0)(4,0)(5,0)]
knop return [(3,0)(4,0),(4,1),(4,2)]
The char in the string are not unique and I need to save the movement for example move one char down or move one char right.
is not a diagonal word finder.
I'm guessing characters have to be directly connected. You save the indices of each character in l in a dict, where key is the character and value is a list of indices tuples. Then you could loop through the words in word and do something like DFS from each position in the list of word's first character (going right or down). An example (follow the comments):
l = ["abc",
"def",
"hij",
"klm",
"nop",
"qrs"]
word = ["abc","knq","knop"]
# save the indices of each character
indices = {}
for i, w in enumerate(l):
for j, c in enumerate(w):
indices[c] = indices.get(c, []) + [(i, j)]
def check(i, j, k, cur, w, tmp):
# if current word matches return the list of indices
if cur == w:
return tmp
# otherwise return false if we reach either end of l
if i == len(l) or j == len(l[i]):
return False
# if the current position is inside l and the character maches the k'th character of w
if l[i][j] == w[k]:
# add the current character and appent it's position to tmp
cur += l[i][j]
tmp.append((i, j))
# check the next position either right or down
chk1 = check(i+1, j, k+1, cur, w, tmp)
if (chk1):
return chk1
chk2 = check(i, j+1, k+1, cur, w, tmp)
if (chk2):
return chk2
return False
# loop through each word and then through each position of the first character
for w in word:
for idx in indices[w[0]]:
# if word is found print the indices and break
chk = check(idx[0], idx[1], 0, '', w, [])
if chk:
print(w, chk)
break
output
abc [(0, 0), (0, 1), (0, 2)]
knq [(3, 0), (4, 0), (5, 0)]
knop [(3, 0), (4, 0), (4, 1), (4, 2)]
If you have a lot of words and build those type of list repeatedly, I would build a translation dictionary first:
tr = dict()
for r,w in enumerate(l):
for c,ch in enumerate(w):
tr[ch] = (r,c)
Having that, you can easily create the lists with a list comprehension:
for w in word:
res = [tr[ch] for ch in w]
print(w)
print(res)
OUTPUT:
abc
[(0, 0), (0, 1), (0, 2)]
knq
[(3, 0), (4, 0), (5, 0)]
knop
[(3, 0), (4, 0), (4, 1), (4, 2)]
What I have now:
d = 0
res = 0
newlist = []
l = [4, 1, 6, 1, 1, 1]
for el in range(len(l)):
for j in range(len(l)):
if abs(l[el] - l[j]) <= d and el != j and el not in newlist and j not in newlist:
newlist.append(el)
newlist.append(j)
res += 1
print(res)
It works well and returns 2 which is correct(1,1; 1,1) but takes too much time. How can I make it work faster ? Thanks.
For example if list = [1, 1, 1, 1] and d = 0 there will be 2 pairs because you can use each number only once. Using (a, b) and (b, c) is not allowed and (a, b) with (b, a) is the same pair...
Sort the list, then walk through it.
Once you have the list sorted, you can just be greedy: take the earliest pair that works, then the next, then the next... and you will end up with the maximum number of valid pairs.
def get_pairs(lst, maxdiff):
sl = sorted(lst) # may want to do lst.sort() if you don't mind changing lst
count = 0
i = 1
N = len(sl)
while i < N:
# no need for abs -- we know the previous value is not bigger.
if sl[i] - sl[i-1] <= maxdiff:
count += 1
i += 2 # these two values are now used
else:
i += 1
return count
And here's some code to benchmark it:
print('generating list...')
from random import randrange, seed
seed(0) # always same contents
l = []
for i in range(1000000):
l.append(randrange(0,5000))
print('ok, measuring...')
from time import time
start = time();
print(get_pairs(l, 0))
print('took', time()-start, 'seconds')
And the result (with 1 million values in list):
tmp$ ./test.py
generating list...
ok, measuring...
498784
took 0.6729779243469238 seconds
You may want to compute all the pairs separately and then collect the pairs you want.
def get_pairs(l, difference):
pairs = []
# first compute all pairs: n choose 2 which is O(n^2)
for i in xrange(len(l)):
for j in xrange(i+1, len(l)):
pairs.append((l[i], l[j]))
# collect pairs you want: O(n^2)
res = []
for pair in pairs:
if abs(pair[0] - pair[1]) <= difference:
res.append(pair)
return res
>>> get_pairs([1,2,3,4,2], 0)
>>> [(2, 2)]
>>> get_pairs([1,2,3,4,2], 1)
>>> [(1, 2), (1, 2), (2, 3), (2, 2), (3, 4), (3, 2)]
If you want to remove duplicates from you result, you can convert the res list to a set before you return it with set(res).
I have a list as follows:
l = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
I want to determine the length of a sequence of equal items, i.e for the given list I want the output to be:
[(0, 6), (1, 6), (0, 4), (2, 3)]
(or a similar format).
I thought about using a defaultdict but it counts the occurrences of each item and accumulates it for the entire list, since I cannot have more than one key '0'.
Right now, my solution looks like this:
out = []
cnt = 0
last_x = l[0]
for x in l:
if x == last_x:
cnt += 1
else:
out.append((last_x, cnt))
cnt = 1
last_x = x
out.append((last_x, cnt))
print out
I am wondering if there is a more pythonic way of doing this.
You almost surely want to use itertools.groupby:
l = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
answer = []
for key, iter in itertools.groupby(l):
answer.append((key, len(list(iter))))
# answer is [(0, 6), (1, 6), (0, 4), (2, 3)]
If you want to make it more memory efficient, yet add more complexity, you can add a length function:
def length(l):
if hasattr(l, '__len__'):
return len(l)
else:
i = 0
for _ in l:
i += 1
return i
l = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
answer = []
for key, iter in itertools.groupby(l):
answer.append((key, length(iter)))
# answer is [(0, 6), (1, 6), (0, 4), (2, 3)]
Note though that I have not benchmarked the length() function, and it's quite possible it will slow you down.
Mike's answer is good, but the itertools._grouper returned by groupby will never have a __len__ method so there is no point testing for it
I use sum(1 for _ in i) to get the length of the itertools._grouper
>>> import itertools as it
>>> L = [0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,2,2,2]
>>> [(k, sum(1 for _ in i)) for k, i in it.groupby(L)]
[(0, 6), (1, 6), (0, 4), (2, 3)]