I have 2 Python lists:
list_a = [[['Ab'], ['Qr', 'Zr']], [['Gt', 'Mh', 'Nt'], ['Dv', 'Cb']]]
list_b = [['Ab', 'QrB', 'Zr'], ['GtB', 'MhB', 'Nt6B', 'DvB', 'Cb6B5']]
I need to un-flatten list_b based on list_a. I need:
list_c = [['Ab'], ['QrB', 'Zr'], [['GtB', 'MhB', 'Nt6B'], ['DbB', 'Cb6B5']]]
Is there a way to get this list_c?
Additional Information:
The lists will always be defined such that:
A partial string from list_a will always be found in list_b. eg. for Gt in one list, there will be either Gt or GtB in the 2nd list.
Entries in each list cannot be in a different order - i.e. if Qr comes before Zr in one list then it (Qr or QrB) must come before Zr in the 2nd list.
Each list can have a maximum of 20 strings in it.
Each list has only unique strings.. eg. Gt cannot occur 2 or more times in any list.
Attempt:
Here is what I have tried:
list_c = [[],[]]
for ty,iten in enumerate(list_b):
for q in iten:
for l_e in list_a:
for items in l_e:
for t,qr in enumerate(items):
if qr in q:
list_c[ty].append([q])
the output of this is:
[[['Ab'], ['QrB'], ['Zr']], [['GtB'], ['MhB'], ['Nt6B'], ['DbB'], ['Cb6B5']]]
The problem is that ['QrB'], ['Zr'] should be combined ['QrB','Zr'] just like they are combined in list_a.
Attempt 2:
for ty,iten in enumerate(list_b):
for q in iten:
for l_e,m in enumerate(list_a):
for ss,items in enumerate(m):
for t,qr in enumerate(items):
if qr in q:
list_a[l_e][ss][t] = q
This works and produces the required output:
[[['Ab'], ['QrB', 'Zr']], [['GtB', 'MhB', 'Nt6B'], ['DvB', 'Cb6B5']]]
However, it (attempt 2) is too long and I would like to know: it does not seem that this is the proper way to do this in Python. Is there is a more Pythonic way to do this?
If all you care about is the length of the sublists in list_a then can transform list_a into its sublist lengths and then use that to slice the sublists of list_b:
# Transform list_a into len of sublists, (generator of generators :)
index_a = ((len(l2) for l2 in l1) for l1 in list_a))
list_c = []
for flatb, index in zip(list_b, index_a):
splitb = []
s = 0
for i in index:
splitb.append(flatb[s:s+i])
s += i
list_c.append(splitb)
Value of list_c:
[[['Ab'], ['QrB', 'Zr']], [['GtB', 'MhB', 'Nt6B'], ['DvB', 'Cb6B5']]]
This is a recursive variant for arbitrary depth of nesting. Not too pretty, but should work.
list_a = [[['Ab'], ['Qr', 'Zr']], [['Gt', 'Mh', 'Nt'], ['Dv', 'Cb']]]
list_b = [['Ab', 'QrB', 'Zr'], ['GtB', 'MhB', 'Nt6B', 'DvB', 'Cb6B5']]
def flatten(l):
for el in l:
if isinstance(el, list):
for sub in flatten(el):
yield sub
else:
yield el
def flitten(l1, l2, i):
result = []
for j in l1:
if isinstance(j, list):
i, res = flitten(j, l2, i)
result.append(res)
else:
result.append(l2[i])
i += 1
return i, result
def flutten(l1, l2):
i, result = flitten(l1, list(flatten(l2)), 0)
return result
print(flutten(list_a, list_b))
# prints [[['Ab'], ['QrB', 'Zr']], [['GtB', 'MhB', 'Nt6B'], ['DvB', 'Cb6B5']]]
Your code does not look too long given the fairly complicated nature of the task (find a list within a list within a list and match it to a list within another list according to the first two letters, and replace the original value with the matched value retaining the nested structure of the original list...)
You could at least eliminate one of the loops like this:
for sub_a, sub_b in zip(list_a, list_b):
for inner_a in sub_a:
for i, a in enumerate(inner_a):
for b in sub_b:
if b.startswith(a):
inner_a[i] = b
If you want a more general solution it will probably involve recursion as in #Tibor's answer.
EDIT: Given the extra information you have supplied, you could recursively work through list_a, replacing all the short strings with their full versions from an iterator based on the flattened version of list_b. This uses the fact that the strings appear in the same order in both lists with no duplicates.
def replace_abbreviations(L, full_names):
for i, item in enumerate(L):
if isinstance(item, basestring):
L[i] = full_names.next()
elif isinstance(item, list):
replace_abbreviations(item, full_names)
replace_abbreviations(list_a, (item for L in list_b for item in L))
Alternatively you can get a flattened list of the indices of each string in both lists and then loop through those:
def flat_indices(L):
for i, item in enumerate(L):
if isinstance(item, list):
for j, inner_list in flat_indices(item):
yield (j, inner_list)
else:
yield (i, L)
for (a, i), (b, j) in zip(flat_indices(list_a), flat_indices(list_b)):
a[i] = b[j]
Related
I am trying to figure out the following:
I take as input a list of lists and an integer N and the objective is to repeat the elements of the sublists N times by adding in front of each element a number in the range of N in increasing order.
For example as input we would receive:
>>> conversion([['GREEN', 'PURPLE'], ['RED']], 3)
[['GREEN0', 'GREEN1', 'GREEN2', 'PURPLE0', 'PURPLE1', 'PURPLE2'], ['RED0', 'RED1', 'RED2']]
My idea was creating an empty list, then adding each element of each sublist in this empty list. Then I would replace the elements with the same version and add range(N) at the end of it but in writing the code I either cannot separate the elements of sublists in the new list or cannot add or separate the different copies of the elements and the values in range(N)
I hope my explanation was clear and thank you for any help possible.
Your idea of a new list is correct. Here is one possible solution.
def conversion(l, n):
new_list = []
for inner_list in l:
new_list.append([])
for word in inner_list:
for i in range(n):
new_list[-1].append(word + str(i))
return new_list
One-liner alternative. (Just for fun):
def conversion(l, n):
return [[word + str(i) for word in inner_list for i in range(n)] for inner_list in l ]
def conversion(input_list, count):
output_list = [
[
input_list[k][j] + str(i)
for j in range(len(input_list[k]))
for i in range(count)
]
for k in range(len(input_list))
]
return output_list
It may be useful to separate the two tasks into smaller functions to make it easier to understand. use listrepeater() to achieve the desired outcome of a list, and then run it in nestedListRepeater()
def listrepeater(l,multiplier):
output = []
for val in l:
for i in range(multiplier):
output.append(val + str(i))
i+=1
return output
def nestedListRepeater(inputlist,multiplier):
outputlist = []
for sublist in inputlist:
outputlist.append(listrepeater(sublist,multiplier))
return outputlist
nestedListRepeater([['GREEN', 'PURPLE'], ['RED']],3)
Given a list of duplicate numbers, how to find a number that repeats three times in a row. For example:
l1 = [1,1,2,2,3,3,3,4,4]
I want to print element number 3 as it appears three times in a row.
I have tried using Counter, which converts this to dict, but not sure how to just print the key that as the max count value.
Using a list comprehension with zip:
L = [1,1,2,2,3,3,3,4,4]
res = [i for i, j, k in zip(L, L[1:], L[2:]) if i == j == k] # [3]
Generalised for an arbitrary number of repeats, you can use list slicing:
n = 3
res = [L[idx] for idx in range(len(L)-n) if len(set(L[idx: idx+n])) == 1] # [3]
Here, Easy. That should help:
d={i:L.count(i) for i in L if L.count(i)>2}
This return a dict with the number that are repeated more than2 times.In your case:
{3: 3}
I am a fresh python beginner and trying to extract the first element of the first n-th elements of a nested list. But it doesn't work so far.
Say:
list = [["Anna","w",15],["Peter","m",20],["Zani","m",10], ["Lily","w",19]]
Goal:
list_new = ['Anna','Peter','Zani'...(#until the n-th elements)]
If I want to first element of all the elements in the list, it should be:
for i in list:
for j in i:
print j[0]
But what if i only want to strip the first element of the n-th elements of the list, instead of all the elements.
For example for the first 2 elements:
I tried:
for i in list[0:2]:
for j in i:
print j[0]
but it didn't work.
What's more, if i want to give the value of n later by using
def sheet(list, n)
and the return statement, how could i do it?
Thank you very much!!
lst = [["Anna","w",15],["Peter","m",20],["Zani","m",10], ["Lily","w",19]]
n = 2
print(lst[n][0])
Output:
Zani
---Updated answer for the details added in the question---
l = [["Anna","w",15],["Peter","m",20],["Zani","m",10], ["Lily","w",19]]
n = 2
for i in range(n):
print(l[i][0])
Output:
Anna
Peter
You can use a list comprehension. Given an input list of lists L:
L_new = [i[0] for i in L[:n]]
Functionally, you can use operator.itemgetter. In addition, you can use itertools.islice to avoid creating an intermediary list:
from operator import itemgetter
from itertools import islice
L_new = list(map(itemgetter(0), islice(L, 0, n)))
Be careful, list is reserved for the built-in function list() in python, you should not use 'list' as variable name.
To get the first element of each nested list :
l = [["Anna","w",15],["Peter","m",20],["Zani","m",10], ["Lily","w",19]]
for e in l:
print(e[0])
Prints :
Anna
Peter
Zani
Lily
To do the same on the nth first elements :
def sheet(l, n):
return [e[0] for e in l[:n]]
sheet(l, 3)
Returns
['Anna', 'Peter', 'Zani']
EDIT
def sheet(l, n):
for e in l[:n]
return [e[0]]
This code only returns ['Anna'] because the return statement stops the function. The for loop is stopped at the first element.
def sheet(l, n):
return [e[0] for e in l[:n]]
is equivalent to :
def sheet(l, n):
result = [e[0] for e in l[:n]]
return result
which is equivalent to :
def sheet(l, n):
result = []
for e in l[:n]:
result.append(e[0])
return result
The for loop can ends before the return statement.
More informations here.
Just try this one which will be easier for you to understand:
n = int(input())
lst = [["Anna","w",15],["Peter","m",20],["Zani","m",10], ["Lily","w",19]]
lst_new = []
for item in lst[:n]:
name, *rest = item
lst_new.append(name)
I have a list of lists of strings, like this:
l = [['apple','banana','kiwi'],['chair','table','spoon']]
Given a string, I want its index in l. Experimenting with numpy, this is what I ended up with:
import numpy as np
l = [['apple','banana','kiwi'],['chair','table','spoon']]
def ind(s):
i = [i for i in range(len(l)) if np.argwhere(np.array(l[i]) == s)][0]
j = np.argwhere(np.array(l[i]) == s)[0][0]
return i, j
s = ['apple','banana','kiwi','chair','table','spoon']
for val in s:
try:
print val, ind(val)
except IndexError:
print 'oops'
This fails for apple and chair, getting an indexerror. Also, this just looks bad to me. Is there some better approch to doing this?
Returns a list of tuples containing (outer list index, inner list index), designed such that the item you're looking for can be in multiple inner lists:
l = [['apple','banana','kiwi'],['chair','table','spoon']]
def findItem(theList, item):
return [(ind, theList[ind].index(item)) for ind in xrange(len(theList)) if item in theList[ind]]
findItem(l, 'apple') # [(0, 0)]
findItem(l, 'spoon') # [(1, 2)]
If you want to use numpy, you don't need to roll your own:
import numpy as np
l = np.array([['apple','banana','kiwi'],['chair','table','spoon']])
s = ['apple','banana','kiwi','chair','table','spoon']
for a in s:
arg = np.argwhere(l==a)
print a, arg, tuple(arg[0]) if len(arg) else None
l = [['apple','banana','kiwi'],['chair','table','spoon']]
def search(lst, item):
for i in range(len(lst)):
part = lst[i]
for j in range(len(part)):
if part[j] == item: return (i, j)
return None
I'd create a dictionary to map the items to their indices:
>>> import numpy as np
>>> l = [['apple','banana','kiwi'],['chair','table','spoon']]
>>> a = np.array(l,dtype=object)
>>> a
array([[apple, banana, kiwi],
[chair, table, spoon]], dtype=object)
>>> d = {s:idx for (idx),s in np.ndenumerate(a)}
>>> d['apple']
(0, 0)
>>> d['chair']
(1, 0)
numpy + ndenumerate is nice for creating the index, but it's definitely not necessary. Of course, this is going to be most efficient if you can create the index once and then reuse it for subsequent searches.
One way is to make use of enumerate:
l = [['apple','banana','kiwi'],['chair','table','spoon']]
s = ['apple','banana','kiwi','chair','table','spoon']
for a in s:
for i, ll in enumerate(l):
for j, b in enumerate(ll):
if a == b:
print a, i, j
In your line that computes i, you already have the answer if you apply argwhere to the entire list, rather than each sublist. There is no need to search again for j.
def ind(s):
match = np.argwhere(np.array(l == s))
if match:
i, j = match[0]
else:
return -1, -1
This is will return the indeces of the first occurence of the string you're searching for.
Also, you might consider how this method is impacted as the complexity of the problem increases. This method will iterate over every element of your list, so the runtime cost increases as the list becomes bigger. So, if the number of test strings you're trying to find in the list also increases, you might want to think about using a dictionary to create a lookup table once, then subsequent searches for test strings are cheaper.
def make_lookup(search_list):
lookup_table = {}
for i, sublist in enumerate(list):
for j, word in enumerate(sublist):
lookup_table[word] = (i, j)
return lookup_table
lookup_table = make_lookup(l)
def ind(s):
if s in lookup_table:
return lookup_table[s]
else:
return -1, -1
To get index of list of list in python:
theList = [[1,2,3], [4,5,6], [7,8,9]]
for i in range(len(theList)):
if 5 in theList(i):
print("[{0}][{1}]".format(i, theList[i].index(5))) #[1][1]
This solution will find all occurrences of the string you're searching for:
l = [['apple','banana','kiwi','apple'],['chair','table','spoon']]
def findItem(theList, item):
return [(i, j) for i, line in enumerate(theList)
for j, char in enumerate(line) if char == item]
findItem(l, 'apple') # [(0, 0), (0, 3)]
findItem(l, 'spoon') # [(1, 2)]
Can anyone tell me how can I call for indexes in a nested list?
Generally I just write:
for i in range (list)
but what if I have a list with nested lists as below:
Nlist = [[2,2,2],[3,3,3],[4,4,4]...]
and I want to go through the indexes of each one separately?
If you really need the indices you can just do what you said again for the inner list:
l = [[2,2,2],[3,3,3],[4,4,4]]
for index1 in xrange(len(l)):
for index2 in xrange(len(l[index1])):
print index1, index2, l[index1][index2]
But it is more pythonic to iterate through the list itself:
for inner_l in l:
for item in inner_l:
print item
If you really need the indices you can also use enumerate:
for index1, inner_l in enumerate(l):
for index2, item in enumerate(inner_l):
print index1, index2, item, l[index1][index2]
Try this setup:
a = [["a","b","c",],["d","e"],["f","g","h"]]
To print the 2nd element in the 1st list ("b"), use print a[0][1] - For the 2nd element in 3rd list ("g"): print a[2][1]
The first brackets reference which nested list you're accessing, the second pair references the item in that list.
You can do this. Adapt it to your situation:
for l in Nlist:
for item in l:
print item
The question title is too wide and the author's need is more specific. In my case, I needed to extract all elements from nested list like in the example below:
Example:
input -> [1,2,[3,4]]
output -> [1,2,3,4]
The code below gives me the result, but I would like to know if anyone can create a simpler answer:
def get_elements_from_nested_list(l, new_l):
if l is not None:
e = l[0]
if isinstance(e, list):
get_elements_from_nested_list(e, new_l)
else:
new_l.append(e)
if len(l) > 1:
return get_elements_from_nested_list(l[1:], new_l)
else:
return new_l
Call of the method
l = [1,2,[3,4]]
new_l = []
get_elements_from_nested_list(l, new_l)
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
def flatten(lists):
results = []
for numbers in lists:
for numbers2 in numbers:
results.append(numbers2)
return results
print flatten(n)
Output: n = [1,2,3,4,5,6,7,8,9]
I think you want to access list values and their indices simultaneously and separately:
l = [[2,2,2],[3,3,3],[4,4,4],[5,5,5]]
l_len = len(l)
l_item_len = len(l[0])
for i in range(l_len):
for j in range(l_item_len):
print(f'List[{i}][{j}] : {l[i][j]}' )