Are these two generator expressions doing the same thing? - python

Assuming that there is a list to work on, I am not sure whether these two lines of code have the same return values:
sum(lst[i] for i in lst[:-1] if lst[i]<0)
sum(lst[i] for i in range(len(lst)-1) if lst[i]<0)
Furthermore, could I have replaced sum(lst[i]... with sum(i... and still get the exact same result?

In the first you are looping over the elements of lst; do not use those values as an index. Instead, just use the values directly:
sum(elem for elem in lst[:-1] if elem < 0)
I renamed i to elem to make this clearer; now it the equivalent of your second version, where you use indices generated by range().
When you already have a sequence, and need to iterate over the values, there rarely is a need to use range() to produce indices instead.

Related

How to replace using for loop in range(len) by using enumerate

Is there a simple way to use enumerate instead of for loop with range(len)? For example, here I loop to replace all values of each element in subarrays by the index of its subarray.
list = []
for i in range(len(nparray)):
j = [i]*(len(nparray[i]))
list.append(j)
My nparray is np.array with 6 subarrays, and each subarray has different size.
enumerate won't replace the use of for, just make it arguably nicer. You can use list comprehension however:
[[i]*len(x) for i,x in enumerate(nparray)]
And avoid using list as variable name since it's alrteady used as an builtin.
to use enumerator, first you need to declare two target vars because enumerator return a tuple.
Using your example in a comprehension list, it could be like this:
listR = [[idx]*(len(val)) for idx,val in enumerate(multiarray)]
If you want to deep https://docs.python.org/2/library/functions.html#enumerate
I hope this help you.
Regards

Append select dict keys to a list [duplicate]

I am trying to append objects to the end of a list repeatedly, like so:
list1 = []
n = 3
for i in range(0, n):
list1 = list1.append([i])
But I get an error like: AttributeError: 'NoneType' object has no attribute 'append'. Is this because list1 starts off as an empty list? How do I fix this error?
This question is specifically about how to fix the problem and append to the list correctly. In the original code, the reported error occurs when using a loop because .append returns None the first time. For why None is returned (the underlying design decision), see Why do these list operations return None, rather than the resulting list?.
If you have an IndexError from trying to assign to an index just past the end of a list - that doesn't work; you need the .append method instead. For more information, see Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add elements to a list?.
If you want to append the same value multiple times, see Python: Append item to list N times.
append actually changes the list. Also, it takes an item, not a list. Hence, all you need is
for i in range(n):
list1.append(i)
(By the way, note that you can use range(n), in this case.)
I assume your actual use is more complicated, but you may be able to use a list comprehension, which is more pythonic for this:
list1 = [i for i in range(n)]
Or, in this case, in Python 2.x range(n) in fact creates the list that you want already, although in Python 3.x, you need list(range(n)).
You don't need the assignment operator. append returns None.
append returns None, so at the second iteration you are calling method append of NoneType. Just remove the assignment:
for i in range(0, n):
list1.append([i])
Mikola has the right answer but a little more explanation. It will run the first time, but because append returns None, after the first iteration of the for loop, your assignment will cause list1 to equal None and therefore the error is thrown on the second iteration.
I personally prefer the + operator than append:
for i in range(0, n):
list1 += [[i]]
But this is creating a new list every time, so might not be the best if performance is critical.
Note that you also can use insert in order to put number into the required position within list:
initList = [1,2,3,4,5]
initList.insert(2, 10) # insert(pos, val) => initList = [1,2,10,3,4,5]
And also note that in python you can always get a list length using method len()
Like Mikola said, append() returns a void, so every iteration you're setting list1 to a nonetype because append is returning a nonetype. On the next iteration, list1 is null so you're trying to call the append method of a null. Nulls don't have methods, hence your error.
use my_list.append(...)
and do not use and other list to append as list are mutable.

Find index of a sublist in a list

Trying to find the index of a sublists with an element. I’m not sure how to specify the problem exactly (which may be why I’ve overlooked it in a manual), however my problem is thus:
list1 = [[1,2],[3,4],[7,8,9]]
I want to find the first sub-list in list1 where 7 appears (in this case the index is 2, but lll could be very very long). (It will be the case that each number will appear in only 1 sub-list – or not at all. Also these are lists of integers only)
I.e. a function like
spam = My_find(list1, 7)
would give spam = 2
I could try looping to make a Boolean index
[7 in x for x in lll]
and then .index to find the 'true' - (as per Most efficient way to get indexposition of a sublist in a nested list)
However surely having to build a new boolean list is really inefficient..
My code starts with list1 being relatively small, however it keeps building up (eventually there will be 1 million numbers arranged in approx. 5000 sub-lists of list1
Any thoughts?
I could try looping to make a Boolean index
[7 in x for x in lll]
and then .index to find the 'true' … However surely having to build a new boolean list is really inefficient
You're pretty close here.
First, to avoid building the list, use a generator expression instead of a list comprehension, by just replacing the [] with ().
sevens = (7 in x for x in lll)
But how do you do the equivalent of .index when you have an arbitrary iterable, instead of a list? You can use enumerate to associate each value with its index, then just filter out the non-sevens with filter or dropwhile or another generator expression, then next will give you the index and value of the first True.
For example:
indexed_sevens = enumerate(sevens)
seven_indexes = (index for index, value in indexed_sevens if value)
first_seven_index = next(seven_indexes)
You can of course collapse all of this into one big expression if you want.
And, if you think about it, you don't really need that initial expression at all; you can do that within the later filtering step:
first_seven_index = next(index for index, value in enumerate(lll) if 7 in value)
Of course this will raise a StopIteration exception instead of a ValueError expression if there are no sevens, but otherwise, it does the same thing as your original code, but without building the list, and without continuing to test values after the first match.

How do I check if all elements in a list are the same?

If i have this list;
mylist = ['n', 'n', '4', '3', 'w']
How do I get it to read the list, and tell me whether or not they are all the same?
I am aware that it is easy to tell they are not all the same in this example. I have much larger lists I would like it to read for me.
Would I go about this using:
min(...)
If so, how would I input each list item?
You can use set like this
len(set(mylist)) == 1
Explanation
sets store only unique items in them. So, we try and convert the list to a set. After the conversion, if the set has more than one element in it, it means that not all the elements of the list are the same.
Note: If the list has unhashable items (like lists, custom classes etc), the set method cannot be used. But we can use the first method suggested by #falsetru,
all(x == mylist[0] for x in mylist)
Advantages:
It even works with unhashable types
It doesn't create another temporary object in memory.
It short circuits after the first failure. If the first and the second elements don't match, it returns False immediately, whereas in the set approach all the elements have to be compared. So, if the list is huge, you should prefer the all approach.
It works even when the list is actually empty. If there are no elements in the iterable, all will return True. But the empty list will create an empty set for which the length will be 0.
Using all and generator expression:
all(x == mylist[0] for x in mylist)
Alternative:
mylist.count(mylist[0]) == len(mylist)
NOTE The first will stop as soon as it found there's any different item in the list, while the alternative will not.

list index out of range error while iterating in list length

I have the following code, which sortings_list consist of 2 items like
sortings_list = ['code', 'name']
for i in xrange(0, len(sortings_list)):
if sortings_list[i] == '-%s' % field:
sortings_list.pop(i)
Any ideas ?
You are removing items from a list while iterating, if you remove the first item then the second item's index changes. Use a list comprehension instead:
sortings_list = [elem for elem in sortings_list if not elem == '-%s' % field]
You're calling pop() on the first item which removes it, and now the list only has one element.
Then you try to iterate to the second item, which doesn't exist anymore.
You are better off using list comprehension because indexing is messy. With Python, you don't need to index a list in most cases. That being said, if you still insist on using your solution:
for i in xrange(len(sortings_list) - 1, -1, -1):
if ...:
sortings_list.pop(i)
That is, you start from the end of the list and traverse backward. That way, all the indexing still works. Again, I highly recommend against doing things this way. Go with list comprehension which Martijn Pieters offers.

Categories

Resources