why __len__() give different output result for s = [] and s=[[]]? - python

code snippet:
>>>s = []
>>>len(s)
0
however:
>>>s = [[]]
>>>len(s)
1
I just declare two lists but did not assign any element, why len() give different output?

You did assign an element. Your second list contains another empty list:
>>> l = [[]]
>>> l
[[]]
>>> len(l)
1
>>> l[0]
[]
>>> len(l[0])
0
If it helps, break down what you did into two steps; create an empty list then create another list with just that one element:
>>> l1 = [] # empty
>>> len(l1)
0
>>> l2 = [l1] # one element
>>> l2
[[]]
>>> len(l2)
1
Other than that we have one more reference to the nested list, the outcome is exactly the same; an empty list contained within another list object.
You could add any number of empty lists inside an outer list; that doesn't make the outer list empty however:
>>> len([[], [], []])
3
because each of those empty lists contained in the outer list is still a separate object.
Note: use the len() built-in function, don't call the __len__ method directly. Python takes care of that detail for you.
If you wanted to know the total lengths of all contained lists, you could use:
sum(len(sub) for sub in outer)
or you could use:
not any(outer)
if you just wanted to know if all contained elements are 'empty' or otherwise considered false.
Demo:
>>> s = [[1, 2], [3, 4]] # not empty
>>> not any(s)
False
>>> sum(len(sub) for sub in s)
4
>>> s = [[], []] # all empty
>>> not any(s)
True
>>> sum(len(sub) for sub in s)
0

In one case you do have an element in your list : an empty list.
s = [[], []]
has two elements for example.
s = [[]]
has one element and
s = []
is empty

[] is an empty list, it has zero elements.
[[]] is a list with exactly one element, an empty list.
>>> for x in []:
... print(x)
...
>>> for x in [[]]:
... print(x)
...
[]
As your can see, the first for loop prints nothing, because there's nothing in []. The second for loop prints [], because there's [] inside [[]].
If you know the concept of sets from math, here's an analogy:
Let x = {} be the empty set. Then the set {x} contains one element (the empty set).

The second list indeed contains an element, which is an empty list.

Related

Why do the list function and list literals act differently?

I was trying to create a list with n empty lists inside of it, but faced an interesting situation.
Firstly I tried the following:
>>> n = 3
>>> list([]) * n
[]
It creates an empty list.
After that I tried the following line, which creates an empty list, too:
>>> list(list()) * n
[]
But when I try the same with literals,
>>> [[]] * n
[[], [], []]
it gives the correct output. Can someone explain why?
list(...) is not interchangeable with [...]. You can wrap square brackets around things to get nested lists, but wrapping things in list() doesn't have the same effect.
When you write [[]] you get a list with a single element. That element is [].
When you write list([]) the list constructor takes an iterable and creates a list with the same elements. If you wrote list(['foo', 'bar']) the result would be ['foo', 'bar']. Writing list([]) yields [].
This form is more useful when you pass iterables that aren't lists. Other examples:
list('abc') == ['a', 'b', 'c'] # strings are iterables over their characters
list(('foo', 'bar')) == ['foo', 'bar'] # tuple
list({'foo', 'bar'}) == ['foo', 'bar'] # set; could get ['bar', 'foo'] instead
list(list()) is equivalent to list([]). And we've seen that's in turn equivalent to [].
Stay away from [[]] * n when creating a multidimensional list. It will create a list with n references to the same list object and will bite you when you try to modify the internal lists. It's a common mistake that everyone makes. Use this instead, which will create a separate empty list for each slot:
[[] for i in range(n)]
Further reading:
How to create a multi-dimensional list
List of lists changes reflected across sublists unexpectedly

How to return True when detecting a nested list of empty lists?

I was trying to return the True value when writing the statement:
list = [[],[],[],[]]
list == []
Instead it returns False
My nested list consists of a variable number of empty lists.
How do I write a single statement that could apply to nested list of [1/2/3...] empty lists?
all(x == [] for x in your_list)
to return True if all empty
any(x != [] for x in your_list)
to return True if at least on is not empty
You could first remove all empty lists and then check if the result equals an empty list, you could do this in a single line as follows:
[x for x in list if x != []] == []
You can make use of all in python to match a condition for all elements in a list. In this case the condition is if the element is an empty list or not.
>>> my_list = [[], [], []]
>>> all([x == [] for x in my_list])
True
If you are sure that all items in your list are lists you can use any directly because the truthy value of [] is False
list_of_lists = [[],[],[],[]]
if not any(list_of_lists):
# all lists are empty (or list_of_lists itself is empty)
Various uses of any and all will allow you to check other similar conditions:
if any(list_of_list):
# at least one of the list is not empty
if all(list_of_list):
# none of the lists are empty
if not all(list_of_list):
# at least one of the lists is empty
I do not completely understand your problem but if you want to detect an empty list or a nested list of empty list try by getting keeping only unique elements (say your list is l)
if l ==[] or list(set(l))==[[]]:
Use any() function.
list = [[], [], []]
any(list) == bool([]) # evaluates to True
In python, bool value of an empty list is False and any function returns False if none of the values inside the list are True.

Python - list comprehension , 2D list

I'm trying to figure out how to delete duplicates from 2D list. Let's say for example:
x= [[1,2], [3,2]]
I want the result:
[1, 2, 3]
in this order.
Actually I don't understand why my code doesn't do that :
def removeDuplicates(listNumbers):
finalList=[]
finalList=[number for numbers in listNumbers for number in numbers if number not in finalList]
return finalList
If I should write it in nested for-loop form it'd look same
def removeDuplicates(listNumbers):
finalList=[]
for numbers in listNumbers:
for number in numbers:
if number not in finalList:
finalList.append(number)
return finalList
"Problem" is that this code runs perfectly. Second problem is that order is important. Thanks
finalList is always an empty list on your list-comprehension even though you think it's appending during that to it, which is not the same exact case as the second code (double for loop).
What I would do instead, is use set:
>>> set(i for sub_l in x for i in sub_l)
{1, 2, 3}
EDIT:
Otherway, if order matters and approaching your try:
>>> final_list = []
>>> x_flat = [i for sub_l in x for i in sub_l]
>>> list(filter(lambda x: f.append(x) if x not in final_list else None, x_flat))
[] #useless list thrown away and consumesn memory
>>> f
[1, 2, 3]
Or
>>> list(map(lambda x: final_list.append(x) if x not in final_list else None, x_flat))
[None, None, None, None] #useless list thrown away and consumesn memory
>>> f
[1, 2, 3]
EDIT2:
As mentioned by timgeb, obviously the map & filter will throw away lists that are at the end useless and worse than that, they consume memory. So, I would go with the nested for loop as you did in your last code example, but if you want it with the list comprehension approach than:
>>> x_flat = [i for sub_l in x for i in sub_l]
>>> final_list = []
>>> for number in x_flat:
if number not in final_list:
finalList.append(number)
The expression on the right-hand-side is evalueated first, before assigning the result of this list comprehension to the finalList.
Whereas in your second approach you write to this list all the time between the iterations. That's the difference.
That may be similar to the considerations why the manuals warn about unexpected behaviour when writing to the iterated iterable inside a for loop.
you could use the built-in set()-method to remove duplicates (you have to do flatten() on your list before)
You declare finalList as the empty list first, so
if number not in finalList
will be False all the time.
The right hand side of your comprehension will be evaluated before the assignment takes place.
Iterate over the iterator chain.from_iterable gives you and remove duplicates in the usual way:
>>> from itertools import chain
>>> x=[[1,2],[3,2]]
>>>
>>> seen = set()
>>> result = []
>>> for item in chain.from_iterable(x):
... if item not in seen:
... result.append(item)
... seen.add(item)
...
>>> result
[1, 2, 3]
Further reading: How do you remove duplicates from a list in Python whilst preserving order?
edit:
You don't need the import to flatten the list, you could just use the generator
(item for sublist in x for item in sublist)
instead of chain.from_iterable(x).
There is no way in Python to refer to the current comprehesion. In fact, if you remove the line finalList=[], which does nothing, you would get an error.
You can do it in two steps:
finalList = [number for numbers in listNumbers for number in numbers]
finalList = list(set(finalList))
or if you want a one-liner:
finalList = list(set(number for numbers in listNumbers for number in numbers))

How can I check if a list exist in a dictionary of lists in the same order

Say I have a dictionary of lists,
C = {}
li = []
li.append(x)
C[ind] = li
And I want to check if another list is a member of this dictionary.
for s in C.values():
s.append(w)
Python checks it for any occurrences of the values in s and the dictionary values. But I want to check if any of the lists in the dictionary is identical to the given list.
How can I do it?
Use any for a list of lists:
d = {1 : [1,2,3], 2: [2,1]}
lsts = [[1,2],[2,1]]
print(any(x in d.values() for x in lsts))
True
d = {1:[1,2,3],2:[1,2]}
lsts = [[3,2,1],[2,1]]
print(any(x in d.values() for x in lsts))
False
Or in for a single list:
lst = [1,2]
lst in d.itervalues()
Python will compare each element of both lists so they will have to have the same order to be equal, even if they have the same elements inside the order must also be the same so a simple comparison will do what you want.
in does the trick perfectly, because it does a comparison with each element behind the scenes, so it works even for mutable elements:
lst in d.values()

python - Common lists among lists in a list

I need to be able to find the first common list (which is a list of coordinates in this case) between a variable amount of lists.
i.e. this list
>>> [[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]]
should return
>>> [3,4]
If easier, I can work with a list of all common lists(coordinates) between the lists that contain the coordinates.
I can't use sets or dictionaries because lists are not hashable(i think?).
Correct, list objects are not hashable because they are mutable. tuple objects are hashable (provided that all their elements are hashable). Since your innermost lists are all just integers, that provides a wonderful opportunity to work around the non-hashableness of lists:
>>> lists = [[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]]
>>> sets = [set(tuple(x) for x in y) for y in lists]
>>> set.intersection(*sets)
set([(3, 4)])
Here I give you a set which contains tuples of the coordinates which are present in all the sublists. To get a list of list like you started with:
[list(x) for x in set.intersection(*sets)]
does the trick.
To address the concern by #wim, if you really want a reference to the first element in the intersection (where first is defined by being first in lists[0]), the easiest way is probably like this:
#... Stuff as before
intersection = set.intersection(*sets)
reference_to_first = next( (x for x in lists[0] if tuple(x) in intersection), None )
This will return None if the intersection is empty.
If you are looking for the first child list that is common amongst all parent lists, the following will work.
def first_common(lst):
first = lst[0]
rest = lst[1:]
for x in first:
if all(x in r for r in rest):
return x
Solution with recursive function. :)
This gets first duplicated element.
def get_duplicated_element(array):
global result, checked_elements
checked_elements = []
result = -1
def array_recursive_check(array):
global result, checked_elements
if result != -1: return
for i in array:
if type(i) == list:
if i in checked_elements:
result = i
return
checked_elements.append(i)
array_recursive_check(i)
array_recursive_check(array)
return result
get_duplicated_element([[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]])
[3, 4]
you can achieve this with a list comprehension:
>>> l = [[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]]
>>> lcombined = sum(l, [])
>>> [k[0] for k in [(i,lcombined.count(i)) for i in lcombined] if k[1] > 1][0]
[3, 4]

Categories

Resources