This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Flatten (an irregular) list of lists in Python
I have a python list whose elements can be letters or lists of letters.I wanted to write a function to extract all elements as below
suppose
l=['a',['b',['c']] ]
The output need to be ['a','b','c']
I thought recursion would be the correct way to do this.
The base case may be that the list contains only one element.
I tried to code this..but the output is
['a', 'b', ['c']]
Can someone please tell me what went wrong here?
def get_all_elements(mylist):
if len(mylist)==1:
return mylist[0]
else:
output=[mylist[0]]
output+=get_all_elements(mylist[1:])
return output
This seems to work Ok:
def flatten(iterable):
out = []
for i in iterable:
if hasattr(i,'__iter__'):
out.extend(flatten(i))
else:
out.append(i)
return out
l=['a',['b',['c']] ]
print flatten(l)
Where you went wrong is that in your solution, mylist[0] can itself be a list (of length 1) which contains another list (of arbitrary length). In that case, you just returned it.
When you check to see if mylist is of length 1, you don't check for the case where its contents are a list. Here's an example that will highlight your problem.
get_all_elements([1, [2, [3, 4]]])
If you want a complete solution, Flattening a shallow list in Python and Comprehension for flattening a sequence of sequences? are good places to look.
this will work on up to 3 levels deep, if the depth is arbitrary i don't think you'll be able to use a list comprehension.
[grandchild for parent in l for child in parent for grandchild in child]
Related
Playing around with python3 REPL and noticed the following:
Why are print( [zip([1,2,3], [3,1,4])] ) and print( list(zip([1,2,3], [3,1,4])) ) different?
The first returns [<zip object at 0xblah>] and the second returns [(1,3), (2,1), (3,4)].
Trying to understand why the list comprehension in the first statement doesn’t give me the result that the list() constructor gives - I think I'm confused about the difference between list comprehension and list() and would appreciate insight into what's happening under the hood.
Searching gives me this question on lists and tuples which doesn't answer my question.
Edit: A suggested question on The zip() function in Python 3 is very helpful background, but does not address the confusion in my question about the difference between a list comprehension and a list literal, so i prefer the submitted answer below as more complete.
The first statement is not a list comprehension, a list comprehension would give you the same result. It is just a list literal containing a zip object:
This would be a list comprehension:
[value for value in zip([1,2,3], [3,1,4])]
The above will print the same as list(zip([1, 2, 3], [3, 1, 4])).
In general, [something] means: A list with one element: something.
On the other hand, list(something) means: Iterate over the values in something, and make a list from the result. You can see the difference for example by putting primitive objects inside it, like a number:
>>> [2]
[2]
>>> list(2)
TypeError: 'int' object is not iterable
This question already has an answer here:
Why a generator object is obtained instead of a list
(1 answer)
Closed 3 years ago.
Not sure if this has been asked before but I couldn't find a proper, clear explanation.
I had a concern about something related to python syntax.
While practicing some python, I Intuitively assumed this would print all the elements of the list; list1.
But it doesn't seem to do so, why would that be?
I could obviously print it in many other ways; but I fail to understand the inherent python logic at play here.
list1 = [1,2,3,4]
print(list1[i] for i in range(len(list1)))
I expected the output to be '[1, 2, 3, 4]', but it instead prints a generator object.
You need to surround list1[i] for i in range(len(list)) with [] to indicate that it's a list. Although list1 is a list, you are trying to use a generator expression to print it out, which will return a generator object (type of iterable similar to a list.) Without specifying you want to convert the generator to a list, it won't print a list. (A generator expression converted to a list is called list comprehension.)
Even if you did do this, it would still print it [1, 2, 3, 4] rather than 1 2 3 4. You need to do [print(list1[i], end=" ") for i in range(len(list1)))] for that to work. There are far better ways of doing this: see donkopotamus's answer.
The expression (list1[i] for i in range(len(list))) defines a generator object. So that is what is printed.
If you wish to print a list, then make it a list comprehension rather than a generator, and print that:
print( [list1[i] for i in range(len(list1))] )
Alternatively, you could force evaluation of the generator into a tuple (or list or set), by passing the generator to the appropriate type using eg
print(tuple(list1[i] for i in range(len(list1))))
In order to get the specific output you intended (space separated) of 1 2 3 4 you could use str.join in the following way:
>>> list1 = [1, 2, 3, 4]
>>> print(" ".join(list1[i] for i in range(len(list1))))
1 2 3 4
or unpack the list into print (this will not work in python 2, as in python 2 print is not a function)
>>> print(*(list1[i] for i in range(len(list1))))
1 2 3 4
(list1[i] for i in range(len(list1)))
is indeed a generator object, equivalent to simply
(x for x in list1)
You're passing that generator to print as a single argument, so print simply prints it: it does not extract the elements from it.
Alternatively, you can unpack it as you pass it to print:
print(*(list1[i] for i in range(len(list1))))
This will pass each element of the generated sequence to print as a separate argument, so they should each get printed.
If you simply meant to print your list, any of the following would have worked:
print(list1)
print([list1[i] for i in range(len(list1))])
print([x for x in list1])
The use of square brackets makes a list comprehension rather than a generator expression.
There is something called list comprehension and generator expression in python. They are awesome tools, you can find more info by googling. Here is a link.
Basically, you can make a list on the fly in python.
list1 = [1,2,3,4]
squared_list = [i*i for i in list1]
would return a list with all the items squared. However, is we say
squared_gen_list = (i*i for i in list1)
this returns what is known as a generator object. That is what is happening in your case, as you can see from the syntax and so you are just printing that out. Hope that clears up the confusion.
Am using Python 2.7.
I have a list of lists like:
testList2 = [[u'462', u'San Germ\xe1n, PR'],[u'461', u'40341']]
I want to encode the strings in the list of lists like:
encodedList = [['462', 'San Germ\xc3\xa1n, PR'],['461', '40341']]
Tried to write a function to do this (did not work):
def testEncode(a):
for list in a:
return [x.encode('utf-8') for x in list]
I think that for the function to work, it needs to append each encoded list to the prior encoded list to generate an encoded list of lists. Not sure how to do this. If someone could explain how the function could be edited to do this, that would be awesome.
I tried the following which did not work either
def testEncode(a):
b = []
for list in a:
b.append([x.encode('utf-8') for x in list])
return b
Having realized that your first code is not actually a typographical error but a logical mistake, let me summarize my comments here. There are two problems (both related) in your approaches:
Problem with the first code: You are currently returning only the first sublist because you put the return in your for loop. Your input list contains sublists so you need to loop over them in a nested manner. One way is to do it as you are doing in your second approach. Another way is to use list comprehensions. Following is the list comprehension way where i will iterate through the sublists and x will iterate through the elements of your sublist i.
def testEncode(a):
return [[x.encode('utf-8') for x in i] for i in a]
Problem with the second code: In this attempt of yours, you have basically solved the problem of ignoring the sublists but you forgot to put your return statement outside the for loop. So before your nested for loop iterate through all the sublists, you prematurely return the result. Therefore, you only see the first sublist modified.
def testEncode(a):
b = []
for list in a:
b.append([x.encode('utf-8') for x in list])
return b # <-- Moved outside the for loop now
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Flatten (an irregular) list of lists in Python
I have a list in python like
l=[1,2,[3,4,5],[[4,2,4],[4,7,8]]]
I want using a set to get all unique values, but this fails
set(l)
TypeError: unhashable type: 'list'
So anybody help please? Want to use set with list of list of list etc etc THX
You'll need to 'unwind', or flatten the nested structure before you can put this in a set. You can use a generator for that to keep this efficient for large lists:
def flatten(lst):
for element in lst:
if isinstance(element, list):
for subelement in flatten(element):
yield subelement
else:
yield element
then use that generator on your list l to create a set:
set(flatten(l))
How about this approach, you flatten the list first before you apply the set operation on it.
import collections
def flat_list(tlist):
if isinstance(tlist, collections.Iterable):
return [j for i in tlist for j in flat_list(i)]
else:
return [tlist]
then:
myl=[1,2,[3,4,5],[[4,2,4],[4,7,8]]]
print set(flat_list(myl))
gives:
set([1, 2, 3, 4, 5, 7, 8])
#MartijnPieters approach with a generator will work more efficiently with very large lists than this list comprehension based approach.
if I have a list, is there any way to check if it contains any other lists?
what i mean to say is, I want to know if a list has this strcuture: [] as opposed to this structure [[]]
so, compare [1,2,3,4] to [1,[2,3],4]
this is complicated by the fact that i have a list of strings.
well, phihag's solution seems to be working so far, but what I'm doing is this:
uniqueCrossTabs = list(itertools.chain.from_iterable(uniqueCrossTabs))
in order to flatten a list if it has other lists in it.
But since my list contains strings, if this is done on an already flattened list, I get a list of each character of each string that was in the original list.
This is not the behavior i was looking for. so, checking to see if the list needs to be flattened before flattening is neccessary.
any(isinstance(el, list) for el in input_list)
You can take phihag's answer even further if you actually want a list of all the lists inside the list:
output_list = filter( lambda x: isinstance(x,list), input_list)
lst1 in lst2
Yields True iff lst1 is in lst2.