Nested list loop indexing - python

I am trying to create a list of characters based on a list of words
i.e.
["BOARD", "GAME"] -> [["B","O"...], ["G","A","M"...]
From my understanding, I have an IndexError because my initial boardlist does not contain a predetermined the amount of lists.
Is there a way for to create a new list in boardlist according to number of objects in board?
I don't know if I'm being clear.
Thank you.
board=["BOARD", "GAME"]
boardlist=[[]]
i=0
for word in board:
for char in word:
boardlist[i].append(char)
i=i+1
print(boardlist)
IndexError: list index out of range

Note that this can be done in a much simpler way by taking a list of each string in the board, as the list constructor will be converting the input iterable, in this case a string, to a list of substrings from it:
l = ["BOARD", "GAME"]
[list(i) for i in l]
# [['B', 'O', 'A', 'R', 'D'], ['G', 'A', 'M', 'E']]
Let's also find a fix to your current approach. Firstly boardlist=[[]] is not a valid way of initializing a list (check what it returns). You might want to check this post. Also instead of incrementing a counter you have enumerate for that:
boardlist = [[] for _ in range(len(board))]
for i, word in enumerate(board):
for char in word:
boardlist[i].extend(char)
print(boardlist)
# [['B', 'O', 'A', 'R', 'D'], ['G', 'A', 'M', 'E']]

Related

How would I turn this array of letters, with choices, into a list of possible words?

7h47 --> should be 'that' in 1337speak
[['j', 't'], ['h'], ['a', 'h'], ['j', 't']]
So the output should be: possible_words = [jhaj, jhat, jhhj, jhht, thaj, that, thhj, thht], just having trouble getting to this step.
What you want is the combination of these lists.
Happily, Python does the hard work for you and everything you need is to use itertools.product:
import itertools
letters = [['j', 't'], ['h'], ['a', 'h'], ['j', 't']]
words = [''.join(x) for x in itertools.product(*letters)]
And you get:
['jhaj', 'jhat', 'jhhj', 'jhht', 'thaj', 'that', 'thhj', 'thht']
In more details, itertools.product returns an iterator that generates all possible combinations of the parameters (a list of letters). Then we iterate over these combinations within a list comprehension and use join to get a word from each list of "chars" (str of length 1 in Python).
If you're a recursion fan, here's an approach using generators:
def combine(combinations, prefix=''):
head, *tail = combinations
# If it's the last word from the combinations
if not tail:
# Yield the full word using the head as suffixes
for letter in head:
yield prefix + letter
else:
# Yield from the tail combinations using the head as suffixes
for letter in head:
yield from combine(tail, prefix + letter)
print(list(combine([['j', 't'], ['h'], ['a', 'h'], ['j', 't']])))
# ['jhaj', 'jhat', 'jhhj', 'jhht', 'thaj', 'that', 'thhj', 'thht']

¿How to get some elements of a sublist into another sublist?

I declare letters which one has many sublists, and I declare max.
What I am trying is to fill a new list stringWithMax with sublist of letters, but in a way that sublists just take the value from 1 to de index that max has in position i. For example, I want to fill stringWithMax[0] with letters[0] but I want to take the range from 1 to max[0], so that when I print stringWithMax it displays in console [['a', 'c', 'b']], then fill stringWithMax[1] with values of letters[1] but just to take values from 1 to max[1], so that when I print again stringWithMax it displays in console [['a', 'c', 'b'], ['F', 'P', 'Z', 'W']] and consecutively get the same with the last line. So in a final way when I print stringWithMax the result in console shows [['a', 'c', 'b'], ['F', 'P', 'Z', 'W'], ['R', 'X', 'N']] and that is exactly as I want.
I have programmed this
letters = [['letters1', 'a', 'c', 'b', 'BUILD'], ['letter2','F', 'P', 'Z', 'W', 'SHOW', 'BUILD'], ['leters3','R', 'X', 'N', 'BUILD', 'SHOW']]
max = [4, 5, 4]
stringWithMax = []
def reGet():
for i in range(len(letters)):
for x in range(1, max[i]):
stringWithMax.append(letters[i][x])
print(stringWithMax)
reGet()
With this code I just get in console ['a', 'c', 'b', 'F', 'P', 'Z', 'W', 'R', 'X', 'N'] which is almost similar for what I want, but it is not a list with sublist as I want.
I hope someone can help me, thanks!
The best for your code is to make use of list comprehension
So your code would be:
letters2 = [[subitem for subitem in item if len(subitem) == 1] for item in letters]
You can use a slice:
def reGet():
for letter, upper in zip(letters, max):
stringWithMax.append(letter[1:upper])
As you can see, I've also improved your code by using zip instead of indices. You can improve it further with a list comprehension:
stringWithMax = [letter[1:upper] for letter, upper in zip(letters, max)]
Alternatively, another case of list comprehensions.
Item is a nested list in list of letters, subitem is each word/letter inside the item. It checks if the length == 1 so you know it's just a letter.
letters2 = [[subitem for subitem in item if len(subitem) == 1] for item in letters]

How do I create a new list with a nested list comprehension?

Say I have a list of words
word_list = ['cat','dog','rabbit']
and I want to end up with a list of letters (not including any repeated letters), like this:
['c', 'a', 't', 'd', 'o', 'g', 'r', 'b', 'i']
without a list comprehension the code would like this:
letter_list=[]
for a_word in word_list:
for a_letter in a_word:
if a_letter not in letter_list:
letter_list.append(a_letter)
print(letter_list)
is there a way to do this with a list comprehension?
I have tried
letter_list = [a_letter for a_letter in a_word for a_word in word_list]
but I get a
NameError: name 'a_word' is not defined
error. I have see answers for similar problems, but they usually iterate over a nested collection (list or tuple). Is there a way to do this from a non-nested list like a_word?
Trying
letter_list = [a_letter for a_letter in [a_word for a_word in word_list]]
Results in the initial list: ['cat','dog','rabbit']
And trying
letter_list = [[a_letter for a_letter in a_word] for a_word in word_list]
Results in:[['c', 'a', 't'], ['d', 'o', 'g'], ['r', 'a', 'b', 'b', 'i', 't']], which is closer to what I want except it's nested lists. Is there a way to do this and have just the letters be in letter_list?
Update. How about this:
word_list = ['cat','dog','rabbit']
new_list = [letter for letter in ''.join(word_list)]
new_list = sorted(set(new_list), key=new_list.index)
print(new_list)
Output:
['c', 'a', 't', 'd', 'o', 'g', 'r', 'b', 'i']
word_list = ['cat','dog','rabbit']
letter_list = list(set([letter for word in word_list for letter in word]))
This works and removes the duplicate letters, but the order is not preserved. If you want to keep the order you can do this.
from collections import OrderedDict
word_list = ['cat','dog','rabbit']
letter_list = list(OrderedDict.fromkeys("".join(word_list)))
you can do it by using list comprehension
l=[j for i in word_list for j in i ]
print(l)
output:
['c', 'a', 't', 'd', 'o', 'g', 'r', 'a', 'b', 'b', 'i', 't']
You can use a list comprehension. It is faster than looping in cases like yours when you call .append on each iteration, as explained by this answer.
But if you want to keep only unique letters (i.e. without repeating any letter), you can use a set comprehension by changing the braces [] to curly braces {} as in
letter_set = {letter for letter in word for word in word_list}
This way you avoid checking the partial list on every iteration to see if the letter is already part of the set. Instead you make use of pythons embedded hashing algorithms and make your code a lot faster.
Another solution:
>>> s = set()
>>> word_list = ['cat', 'dog', 'rabbit']
>>> [c for word in word_list for c in word if (c not in s, s.add(c))[0]]
['c', 'a', 't', 'd', 'o', 'g', 'r', 'b', 'i']
This will test whether the letter is already in the set or not, and it will unconditionally add it to the set (having no effect if it is already present). The None returned from s.add is stored in the temporary tuple but otherwise ignored. The first element of the temporary tuple (that is, the result of the c not in s) is used to filter the items.
This relies on the fact that the elements of the temporary tuple are evaluated from left to right.
Could be considered a bit hacky :-)

Take a formatted list in file to list python

I have a file with a list of letters corresponding to another letter:
A['B', 'D']
B['A', 'E']
C[]
D['A', 'G']
E['B', 'H']
F[]
G['D']
H['E']
I need to import these lists to their corresponding letter, to hopefully have variables that look like this:
vertexA = ['B', 'D']
vertexB = ['A', 'E']
vertexC = []
vertexD = ['A', 'G']
vertexE = ['B', 'H']
vertexF = []
vertexG = ['D']
vertexH = ['E']
What would be the best way to do this? I tried searching for an answer but was unlucky in doing so. Thanks for any help.
You can try using dictionaries rather than variables, and I think it makes it easier as well to populate your data from your textfile.
vertex = {}
vertex['A'] = ['B', 'D']
vertex['A']
>>> ['B', 'D']
When you read your input file, the inputs should look like this:
string='A["B","C"]'
So, we know that the first letter is the name of the list.
import ast
your_list=ast.literal_eval(string[1:])
your_list:
['B', 'C']
You can take care of the looping, reading file, and string manipulation for proper naming...
Building a dictionary would probably be best. Each letter of the alphabet would be a key, and then the value would be a list of associated letters. Here's a proof of concept (not tested):
from string import string.ascii_uppercase
vertices = {}
# instantiate dict with uppercase letters of alphabet
for c in ascii_uppercase:
vertices[c] = []
# iterate over file and populate dict
with open("out.txt", "rb") as f:
for i, line in enumerate(f):
if line[0].upper() not in ascii_uppercase:
# you probably want to do some additional error checking
print("Error on line {}: {}".format(i, line))
else: # valid uppercase letter at beginning of line
list_open = line.index('[')
list_close = line.rindex(']') + 1 # one past end
# probably would want to validate record is in correct format before getting here
# translate hack to remove unwanted chars
row_values = line[list_open:list_close].translate(None, "[] '").split(',')
# do some validation for cases where row_values is empty
vertices[line[0].upper()].extend([e for e in row_values if e.strip() != ''])
Using it would then be easy:
for v in vertices['B']:
# do something with v
File A.txt:
A['B', 'D']
B['A', 'E']
C[]
D['A', 'G']
E['B', 'H']
F[]
G['D']
H['E']
The code:
with open('A.txt','r') as file:
file=file.read().splitlines()
listy=[[elem[0],elem[1:].strip('[').strip(']').replace("'",'').replace(' ','').split(',')] for elem in file]
This makes a nested list, but as Christian Dean said, is a better way to go.
Result:
[['A', ['B', 'D']], ['B', ['A', 'E']], ['C', ['']], ['D', ['A', 'G']], ['E', ['B', 'H']], ['F', ['']], ['G', ['D']], ['H', ['E']]]

Function that retrieves and returns letters from a list of lists

I'm writing a function that needs to go through a list of lists, collect all letters uppercase or lowercase and then return a list with 1 of each letter that it found in order. If the letter appears multiple times in the list of lists the function only has to report the first time it sees the letter.
For example, if the list of lists was [['.', 'M', 'M', 'N', 'N'],['.', '.', '.', '.', 'g'], ['B', 'B', 'B', '.','g']] then the function output should return ["M","N","g","B"].
The code I have so far seems like it could work but it doesn't seem to be working. Any help is appreciated
def get_symbols(lot):
symbols = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
newlot = []
for i in lot:
if i == symbols:
newlot.append(symbols)
return newlot
else:
return None
To build on your existing code:
import string
def get_symbols(lot):
symbols = string.ascii_lowercase + string.ascii_uppercase
newlot = []
for sublot in lot:
for x in sublot:
if x in symbols and x not in newlot:
newlot.append(x)
return newlot
print get_symbols([['.', 'M', 'M', 'N', 'N'],['.', '.', '.', '.', 'g'], ['B', 'B', 'B', '.','g']])
Using string gets us the letters a little more neatly. We then loop over each list provided (each sublot of the lot), and then for each element (x), we check if it is both in our list of all letters and not in our list of found letters. If this is the case, we add it to our output.
There are a few things wrong with your code. You are using return in the wrong place, looping only over the outer list (not over the items in the sublists) and you were appending symbols to newlot instead of the matched item.
def get_symbols(lot):
symbols = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' # You should define this OUTSIDE of the function
newlot = []
for i in lot: # You are iterating over the outer list only here
if i == symbols: # == does not check if an item is in a list, use `in` here
newlot.append(symbols) # You are appending symbols which is the alphabet
return newlot # This will cause your function to exit as soon as the first iteration is over
else:
return None # No need for this
You can use a double for loop and use in to check if the character is in symbols and isn't already in newlot:
l = [['.', 'M', 'M', 'N', 'N'],['.', '.', '.', '.', 'g'], ['B', 'B', 'B', '.','g']]
symbols = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
def get_symbols(lot):
newlot = []
for sublist in lot:
for i in sublist:
if i in symbols and i not in newlot:
newlot.append(i)
return newlot
This is the output for your list:
>>> get_symbols(l)
['M', 'N', 'g', 'B']
this also can be done by using chain, OrderedDict and isalpha as follow
>>> from collections import OrderedDict
>>> from itertools import chain
>>> data = [['.', 'M', 'M', 'N', 'N'],['.', '.', '.', '.', 'g'], ['B', 'B', 'B', '.','g']]
>>> temp = OrderedDict.fromkeys(chain.from_iterable(data))
>>> [x for x in temp if x.isalpha()]
['M', 'N', 'g', 'B']
>>>
chain.from_iterable will serve the same purpose as if you concatenate all the sublist in one
As the order is relevant, OrderedDict will server the same purpose as an set by removing duplicates with the added bonus of preserving the order of the first instance of the object added. The fromkeys class-method will create a dictionary with the given keys and same value, which by default is None, and as we don't care about it, for our purpose is a orderer set
Finally the isalpha will tell you if the string is a letter or not
you can also take a look at the unique_everseen recipe, because itertools is your best friend I recommend to put all those recipes in a file that is always at hand, they always are helpful

Categories

Resources