for loop in the range "first element of lists" - python

I have a list which has the following structure:
a = [[1,'a'], [2,'b'], [3,'c']]
I would like to create a range of the first element in every sub-list without making a second for-loop. I was thinking about something like this:
for i in a[][0]:
print i
However, the above last code does not work (SyntaxError: invalid syntax). Any idea if it's possible to do this in Python?
EDIT:
The output I would like to get with the above loop is:
1
2
3
and not
1
a

for sublist in a:
print sublist[0]
To build a list of first items, use list comprehension:
first_items = [sublist[0] for sublist in a]

for i,_ in a:
print i
should do the trick

This is probably overkill for such a simple example, but for variety:
for i in map(operator.itemgetter(0), a):
print i
In Python 2 map builds the whole list of first elements. You could use itertools.imap to avoid that. Of course for 3 elements it doesn't matter anyway.
I mention this because it's more flexible than for i, _ in a (there don't need to be exactly two elements in each sublist) and it gives you the i you want instead of doing for i in a and using i[0] (perhaps multiple times in a less simple example). But of course you could just as easily get the i you want with:
for l in a:
i = l[0]
print i
... not everything needs to be done in the loop header, it's just nice that it can be :-)

>>> a = [[1,'a'], [2,'b'], [3,'c']]
>>> for i in a:
... print i[0]
...
1
2
3
I think this method is kind of close to what you were trying.

>>> a = [[1,'a'], [2,'b'], [3,'c']]
>>> for [x,y] in a:
... print(x)
...
1
2
3
However,if your lists are of unequal size, then #warwaruk's answer is better.

Related

The same list has different lengths please help me understand [duplicate]

I'm currently developing a program in python and I just noticed that something was wrong with the foreach loop in the language, or maybe the list structure. I'll just give a generic example of my problem to simplify, since I get the same erroneous behavior on both my program and my generic example:
x = [1,2,2,2,2]
for i in x:
x.remove(i)
print x
Well, the problem here is simple, I though that this code was supposed to remove all elements from a list. Well, the problem is that after it's execution, I always get 2 remaining elements in the list.
What am I doing wrong? Thanks for all the help in advance.
Edit: I don't want to empty a list, this is just an example...
This is a well-documented behaviour in Python, that you aren't supposed to modify the list being iterated through. Try this instead:
for i in x[:]:
x.remove(i)
The [:] returns a "slice" of x, which happens to contain all its elements, and is thus effectively a copy of x.
When you delete an element, and the for-loop incs to the next index, you then skip an element.
Do it backwards. Or please state your real problem.
I think, broadly speaking, that when you write:
for x in lst:
# loop body goes here
under the hood, python is doing something like this:
i = 0
while i < len(lst):
x = lst[i]
# loop body goes here
i += 1
If you insert lst.remove(x) for the loop body, perhaps then you'll be able to see why you get the result you do?
Essentially, python uses a moving pointer to traverse the list. The pointer starts by pointing at the first element. Then you remove the first element, thus making the second element the new first element. Then the pointer move to the new second – previously third – element. And so on. (it might be clearer if you use [1,2,3,4,5] instead of [1,2,2,2,2] as your sample list)
Why don't you just use:
x = []
It's probably because you're changing the same array that you're iterating over.
Try Chris-Jester Young's answer if you want to clear the array your way.
I know this is an old post with an accepted answer but for those that may still come along...
A few previous answers have indicated it's a bad idea to change an iterable during iteration. But as a way to highlight what is happening...
>>> x=[1,2,3,4,5]
>>> for i in x:
... print i, x.index(i)
... x.remove(i)
... print x
...
1 0
[2, 3, 4, 5]
3 1
[2, 4, 5]
5 2
[2, 4]
Hopefully the visual helps clarify.
I agree with John Fouhy regarding the break condition. Traversing a copy of the list works for the remove() method, as Chris Jester-Young suggested. But if one needs to pop() specific items, then iterating in reverse works, as Erik mentioned, in which case the operation can be done in place. For example:
def r_enumerate(iterable):
"""enumerator for reverse iteration of an iterable"""
enum = enumerate(reversed(iterable))
last = len(iterable)-1
return ((last - i, x) for i,x in enum)
x = [1,2,3,4,5]
y = []
for i,v in r_enumerate(x):
if v != 3:
y.append(x.pop(i))
print 'i=%d, v=%d, x=%s, y=%s' %(i,v,x,y)
or with xrange:
x = [1,2,3,4,5]
y = []
for i in xrange(len(x)-1,-1,-1):
if x[i] != 3:
y.append(x.pop(i))
print 'i=%d, x=%s, y=%s' %(i,x,y)
If you need to filter stuff out of a list it may be a better idea to use list comprehension:
newlist = [x for x in oldlist if x%2]
for instance would filter all even numbers out of an integer list
The list stored in the memory of a computer. This deals with the pointer to a memory artifact. When you remove an element, in a by-element loop, you are then moving the pointer to the next available element in the memory address
You are modifying the memory and iterating thru the same.
The pointer to the element moves through the list to the next spot available.
So in the case of the Size being 5...enter code here
[**0**,1,2,3,4]
remove 0 ---> [1,**2**,3,4] pointer moves to second index.
remove 2 ---> [1,3,**4**] pointer moves to 3rd index.
remove 4 ---> [1,3]
I was just explaining this to my students when they used pop(1). Another very interesting side-effect error.
x=[1,**2**,3,4,5]
for i in x:
x.pop(1)
print(x,i)
[1, **3**, 4, 5] 1 at index 0 it removed the index 1 (2)
[1, **4**, 5] 3 at index 1 it removed the index 1 (3)
[1, 5] 5 at index 2 it removed the index 1 (4)
heh.
They were like why isnt this working... I mean... it did... exactly what you told it to do. Not a mind reader. :)

Check number not a sum of 2 ints on a list

Given a list of integers, I want to check a second list and remove from the first only those which can not be made from the sum of two numbers from the second. So given a = [3,19,20] and b = [1,2,17], I'd want [3,19].
Seems like a a cinch with two nested loops - except that I've gotten stuck with break and continue commands.
Here's what I have:
def myFunction(list_a, list_b):
for i in list_a:
for a in list_b:
for b in list_b:
if a + b == i:
break
else:
continue
break
else:
continue
list_a.remove(i)
return list_a
I know what I need to do, just the syntax seems unnecessarily confusing. Can someone show me an easier way? TIA!
You can do like this,
In [13]: from itertools import combinations
In [15]: [item for item in a if item in [sum(i) for i in combinations(b,2)]]
Out[15]: [3, 19]
combinations will give all possible combinations in b and get the list of sum. And just check the value is present in a
Edit
If you don't want to use the itertools wrote a function for it. Like this,
def comb(s):
for i, v1 in enumerate(s):
for j in range(i+1, len(s)):
yield [v1, s[j]]
result = [item for item in a if item in [sum(i) for i in comb(b)]]
Comments on code:
It's very dangerous to delete elements from a list while iterating over it. Perhaps you could append items you want to keep to a new list, and return that.
Your current algorithm is O(nm^2), where n is the size of list_a, and m is the size of list_b. This is pretty inefficient, but a good start to the problem.
Thee's also a lot of unnecessary continue and break statements, which can lead to complicated code that is hard to debug.
You also put everything into one function. If you split up each task into different functions, such as dedicating one function to finding pairs, and one for checking each item in list_a against list_b. This is a way of splitting problems into smaller problems, and using them to solve the bigger problem.
Overall I think your function is doing too much, and the logic could be condensed into much simpler code by breaking down the problem.
Another approach:
Since I found this task interesting, I decided to try it myself. My outlined approach is illustrated below.
1. You can first check if a list has a pair of a given sum in O(n) time using hashing:
def check_pairs(lst, sums):
lookup = set()
for x in lst:
current = sums - x
if current in lookup:
return True
lookup.add(x)
return False
2. Then you could use this function to check if any any pair in list_b is equal to the sum of numbers iterated in list_a:
def remove_first_sum(list_a, list_b):
new_list_a = []
for x in list_a:
check = check_pairs(list_b, x)
if check:
new_list_a.append(x)
return new_list_a
Which keeps numbers in list_a that contribute to a sum of two numbers in list_b.
3. The above can also be written with a list comprehension:
def remove_first_sum(list_a, list_b):
return [x for x in list_a if check_pairs(list_b, x)]
Both of which works as follows:
>>> remove_first_sum([3,19,20], [1,2,17])
[3, 19]
>>> remove_first_sum([3,19,20,18], [1,2,17])
[3, 19, 18]
>>> remove_first_sum([1,2,5,6],[2,3,4])
[5, 6]
Note: Overall the algorithm above is O(n) time complexity, which doesn't require anything too complicated. However, this also leads to O(n) extra auxiliary space, because a set is kept to record what items have been seen.
You can do it by first creating all possible sum combinations, then filtering out elements which don't belong to that combination list
Define the input lists
>>> a = [3,19,20]
>>> b = [1,2,17]
Next we will define all possible combinations of sum of two elements
>>> y = [i+j for k,j in enumerate(b) for i in b[k+1:]]
Next we will apply a function to every element of list a and check if it is present in above calculated list. map function can be use with an if/else clause. map will yield None in case of else clause is successful. To cater for this we can filter the list to remove None values
>>> list(filter(None, map(lambda x: x if x in y else None,a)))
The above operation will output:
>>> [3,19]
You can also write a one-line by combining all these lines into one, but I don't recommend this.
you can try something like that:
a = [3,19,20]
b= [1,2,17,5]
n_m_s=[]
data=[n_m_s.append(i+j) for i in b for j in b if i+j in a]
print(set(n_m_s))
print("after remove")
final_data=[]
for j,i in enumerate(a):
if i not in n_m_s:
final_data.append(i)
print(final_data)
output:
{19, 3}
after remove
[20]

about basic Python list indexing

I'm not sure if this has been asked before as it seems to be a simple question, I tried to search it but did not find a very good answer.
Say I have a list of numbers a. I know I can get out every fifth number by a[::5].
What should be the simplest way to get the rest of the list (items in a but not in a[::5])?
Edit:
this is not the same question as Finding elements not in a list as I already know that the rest of the list consists of a[1::5]+a[2::5]+a[3::5]+a[4::5], I'm just asking is there a simpler way / syntax sugar for that?
Edit:
Thank you all for answering.
Is there a solution without scanning over the whole list?
[x for i, x in enumerate(a) if i % 5 != 0]
not in set(a[::5]) will not work correctly when list contains equal elements. not in a[::5] is also not optimal from performance point of view.
Edit There is no syntactic sugar for your problem in Python. Take a look at numpy: it has extensive slicing possibilities.
Edit To avoid list scanning, you could create wrapper class with __getitem__ method.
I would start by something like:
[v for v in a if v not in a[::5]]
Store the unwanted value in a set before could lead to better perf, because not in method is in O(1) for sets:
unwanted = set(a[::5])
[v for v in a if v not in unwanted]
If the amount of data is huge, bloom filter could be a good substitute.
Does this help you?
items = [1,2,3,4]
Z = [3,4,5,6]
a = list(set(items)-set(Z))
# a =[1, 2]
Such as:
fifths = a[::5]
not_fifths = list(set(a)-set(fifths))
temp = [x for x in a if x not in a[::5]]
You can try this:
[a[i-1] for i in range(1, len(a) + 1) if i % 5]
Basically, it checks for every position to see if it is a multiple of 5 or not. You don't have to create a set object this way.
This would do it, without any index look up overheads.
result = [n for i,n in enumerate(a) if i % 5 != 0]

using list comprehension in a IF statement [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Return the first item in a list matching a condition
How to easily grab the "matching" list element using list comprehension? e.g. I've a list and I'm looking for a element that starts with a a certain string. That's easy to do:
>>> lines = ['AHP Buildlife number', 'NIT-Version-Default-v0.16.0', 'E_release v2.3.14.0']
>>> [ x.strip() for x in lines if x.startswith('NIT-Version-Default') ]
['NIT-Version-Default-v0.16.0']
But how can I do the same from in a if statement so that the matching list-element can be used for further processing; some thing like this:
>>> if [ x.strip() for x in lines if x.startswith('NSS-Checkin-Default') ]:
... ver = x.split('-')[-1].strip()
... print ver
So, that it return v0.16.0 as the version number. This obviously doesn't work but hopefully you get the idea what I'm trying to do. Any idea how to do that? Cheers!!
PS. You are welcome to improve the question or the title.
If you're looking for only one element:
x = next(x.strip() for x in lines if x.startswith('NSS-Checkin-Default'))
ver = x.split('-')[-1].strip()
Note that I've used a generator expression (a for b in c if d), instead a list comprehension [a for b in c if d]. This way the iteration over lines will only be done for as long as actually needed (in this case, until the first element is found).
If no element is found, next will raise StopIteration.
If you additionally want to make sure that there's exactly one element (or exactly N elements for some fixed N), you could write it this way (assign to a tuple):
(x,) = (x.strip() for x in lines if x.startswith('NSS-Checkin-Default')
In that case you'll have a ValueError if the generator expression will yield too few or too many elements. This is called "tuple unpacking".
See also:
What is the best way to get the first item from an iterable matching a condition?
Simple: your list comprehension returns a list with one element, so you just access the element: thelist[0]
To take from your example:
lines = ['AHP Buildlife number', 'NIT-Version-Default-v0.16.0', 'E_release v2.3.14.0']
results = [ x.strip() for x in lines if x.startswith('NIT-Version-Default') ]
ver = results[0].split('-')[-1].strip()
print ver
See the map function. I don't see a vital necessity of comprehensions, instead:
map(lambda x: x.split('-')[-1].strip(),
filter(lambda x: x.startswith('NIT'), lines))
>>> for each in lines:
... match=re.search("(^NIT-Version-Default).*(v[0-9.]+)",each)
... if match:
... print match.group(2)
...
v0.16.0

How do I merge a 2D array in Python into one string with List Comprehension?

List Comprehension for me seems to be like the opaque block of granite that regular expressions are for me. I need pointers.
Say, I have a 2D list:
li = [[0,1,2],[3,4,5],[6,7,8]]
I would like to merge this either into one long list
li2 = [0,1,2,3,4,5,6,7,8]
or into a string with separators:
s = "0,1,2,3,4,5,6,7,8"
Really, I'd like to know how to do both.
Like so:
[ item for innerlist in outerlist for item in innerlist ]
Turning that directly into a string with separators:
','.join(str(item) for innerlist in outerlist for item in innerlist)
Yes, the order of 'for innerlist in outerlist' and 'for item in innerlist' is correct. Even though the "body" of the loop is at the start of the listcomp, the order of nested loops (and 'if' clauses) is still the same as when you would write the loop out:
for innerlist in outerlist:
for item in innerlist:
...
Try that:
li=[[0,1,2],[3,4,5],[6,7,8]]
li2 = [ y for x in li for y in x]
You can read it like this:
Give me the list of every ys.
The ys come from the xs.
The xs come from li.
To map that in a string:
','.join(map(str,li2))
There's a couple choices. First, you can just create a new list and add the contents of each list to it:
li2 = []
for sublist in li:
li2.extend(sublist)
Alternately, you can use the itertools module's chain function, which produces an iterable containing all the items in multiple iterables:
import itertools
li2 = list(itertools.chain(*li))
If you take this approach, you can produce the string without creating an intermediate list:
s = ",".join(itertools.chain(*li))
My favorite, and the shortest one, is this:
li2 = sum(li, [])
and
s = ','.join(li2)
EDIT: use sum instead of reduce, (thanks Thomas Wouters!)
For the second one, there is a built-in string method to do that :
>>> print ','.join(str(x) for x in li2)
"0,1,2,3,4,5,6,7,8"
For the first one, you can use join within a comprehension list :
>>> print ",".join([",".join(str(x) for x in li])
"0,1,2,3,4,5,6,7,8"
But it's easier to use itertools.flatten :
>>> import itertools
>>> print itertools.flatten(li)
[0,1,2,3,4,5,6,7,8]
>>> print ",".join(str(x) for x in itertools.flatten(li))
"0,1,2,3,4,5,6,7,8"
N.B : itertools is a module that help you to deal with common tasks with iterators such as list, tuples or string... It's handy because it does not store a copy of the structure you're working on but process the items one by one.
EDIT : funny, I am learning plenty of way to do it. Who said that there was only one good way to do it ?
import itertools
itertools.flatten( li )
To make it a flattened list use either:
http://code.activestate.com/recipes/121294/
http://code.activestate.com/recipes/363051/
Then, join to make it a string.
Here is a way:
def convert2DArrtostring(ndArr):
'''converts 2D array to string'''
arr_str = "["
for i in ndArr:
arr_str += "["
for j in i:
arr_str += str(j) + " "
arr_str += "]\n"
arr_str += "]"
return arr_str
There are many ways to do this problem. I like Numpy's tools because it is normally already imported in everything I do. However, if you aren't using Numpy for anything else this probably isn't a good method.
import numpy
li = [[0,1,2],[3,4,5],[6,7,8]]
li2=li[0] #first element of array to merge
i=1
while i<len(li):
li2=numpy.concatenate((li2,li[i]))
i+=1
print li2
This would print [0 1 2 3 4 5 6 7 8] and then you can convert this into your string too.

Categories