Restrict for loop in Python for specific iterations - python

for this list ,
l=[i for i in range(1,100)]
How can i restrict to print only 1st 20 elements.
What i am trying to do is ,
>>> counter=0
>>> for index , i in enumerate(l):
... if counter==20:
... break
... print index , i
... counter+=1
...
Is there is another way to do this without using counter variable ?

Use a sliced list, like this
l=[i for i in range(1,100)]
for index, i in enumerate(l[:20]):
print index, i
Or you can use itertools.islice, to avoid generating entire list and instead iterate over xrange as long as you want, like this
from itertools import islice
for index, i in enumerate(islice(xrange(1, 100), 20)):
print index, i

Nishant N.'s answer is the probably the best. But your code would also have worked had you changed your if statement to read
if i == 20:
Just in case you wondered why it wasn't working (also you would have needed to set counter to 0 before the code you posted, but I accept that may just have been omitted.

Related

Display only 1 element when it's a repetition

I would like print a result without duplicate with my multiplication
Here an example :
5*3*2=30
2*3*5=30
5*2*3=30
3*2*5=30
.....
All these element are from my list that I browse and you can see it's always =30
So I would like display only the first element (5*3*2) and not others because they are the same.
To be more accurate, here an example what I have :
list = ['5*3*2','5*2*3','2*3*5','2*5*3']
for i in list:
if eval(i) == eval(i)+1 ??? (I dont know how to say the next element)
print(eval(i))
Thanks for reading
Something like this with not in will help you.
#python 3.5.2
list = ['5*3*2','5*2*3','6*9','2*3*5','2*5*3','8*3','9*6']
elm = []
for i in list:
elm_value = eval(i)
if elm_value not in elm:
elm.append(elm_value)
print(elm)
DEMO: https://rextester.com/QKV22875
The comparison:
eval(i) == eval(i)+1
Will compare if the the number i is equal to i + 1, which will always return False. I'm sure you mean to use i as an index and simply wanted to compare if the current element is equal to the next element in the list. However, doing this doesn't really keep track of duplicates, since you have to consider everything else in the list.
Its also not a good idea to use list as a variable name, since it shadows the builtin function list. Plenty of other suitable names you can use.
One way is to use a set to keep track of what items you have seen, and only print items that you have seen for the first time:
lst = ["5*3*2","5*2*3","2*3*5","2*5*3"]
seen = set()
for exp in lst:
calc = eval(exp)
if calc not in seen:
print(calc)
seen.add(calc)
If you are always dealing with simple multiplying expressions with the * operator(no brackets), you could also use functools.reduce and operator.mul instead to multiply the numbers instead of eval here. This will first split the numbers by *, map each number string to an integer, then multiply every element with each other.
from operator import mul
from functools import reduce
lst = ["5*3*2","5*2*3","2*3*5","2*5*3"]
seen = set()
for exp in lst:
numbers = map(int, exp.split("*"))
calc = reduce(mul, numbers)
if calc not in seen:
print(calc)
seen.add(calc)
Output:
30
With the following list:
l = ['5*3*2','5*2*3','2*3*5','2*5*3', '2*2']
(Note that list is already something in python so I wouldn't recommend using that as a variable name)
I would first create a list of unique values:
unique_vals = set(map(eval, list))
set([4, 30])
Then for each unique values get the first match in l:
[next(x for x in l if eval(x) == i) for i in unique_vals]
I get:
['2*2', '5*3*2']
Is that what you want?

How to return a list that is made up of extracted elements from another list in python?

I am building a function to extract all negatives from a list called xs and I need it to add those extracted numbers into another list called new_home. I have come up with a code that I believe should work, however; it is only showing an empty list.
Example input/output:
xs=[1,2,3,4,0,-1,-2,-3,-4] ---> new_home=[1,2,3,4,0]
Here is my code that returns an empty list:
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if num <0:
new_home= new_home+ xs.pop(num)
return
return new_home
Why not use
[v for v in xs if v >= 0]
def extract_negatives(xs):
new_home=[]
for num in range(len(xs)):
if xs[num] < 0:
new_home.append(xs[num])
return new_home
for your code
But the Chuancong Gao solution is better:
def extract_negative(xs):
return [v for v in xs if v >= 0]
helper function filter could also help. Your function actually is
new_home = filter(lambda x: x>=0, xs)
Inside the loop of your code, the num variable doesn't really store the value of the list as you expect. The loop just iterates for len(xs) times and passes the current iteration number to num variable.
To access the list elements using loop, you should construct loop in a different fashion like this:
for element in list_name:
print element #prints all element.
To achieve your goal, you should do something like this:
another_list=[]
for element in list_name:
if(element<0): #only works for elements less than zero
another_list.append(element) #appends all negative element to another_list
Fortunately (or unfortunately, depending on how you look at it) you aren't examining the numbers in the list (xs[num]), you are examining the indexes (num). This in turn is because as a Python beginner you probably nobody haven't yet learned that there are typically easier ways to iterate over lists in Python.
This is a good (or bad, depending on how you look at it) thing, because had your code taken that branch you would have seen an exception occurring when you attempted to add a number to a list - though I agree the way you attempt it seems natural in English. Lists have an append method to put new elements o the end, and + is reserved for adding two lists together.
Fortunately ignorance is curable. I've recast your code a bit to show you how you might have written it:
def extract_negatives(xs):
out_list = []
for elmt in xs:
if elmt < 0:
out_list.append(elmt)
return out_list
As #ChuangongGoa suggests with his rather terse but correct answer, a list comprehension such as he uses is a much better way to perform simple operations of this type.

Iterate through Python list and do something on last element

I'm looking for a pythonic way to iterate through a list and do something on the last (and only the last) element. There are two ways I can see to do this, of which I would guess the second is best:
for item in a_list:
#do something to every element
if a_list.index(item) == len(a_list) - 1:
# do something to the last one
and
for n, item in enumerate(a_list):
#do something to every element
if n == len(a_list) - 1 :
# do something to the last one
However, I wonder if there is a way of doing it without calling len() on a list I'm already iterating over. I'm quite happy, by the way, to be told that this isn't something I should worry about.
for item in lst:
do_something_to(item)
else:
do_something_extra_special_to_last(item)
Here I just assume that you want to do something extra to the last item (the normal action will still be taken on it beforehand). I also assume you aren't hitting any break statements (in that case else won't execute). Of course, you don't really need else:
for item in lst:
do_something_to(item)
do_something_extra_special_to_last(item)
should work too since the loop variable "leaks" into the enclosing scope and if there are breaks that you're worried about and you really are looping over a sequence, why not:
for item in lst:
do_something_to(item)
do_something_extra_special_to_last(lst[-1])
You're making up problems :) There really isn't any with your approach.
If you want to loop, you can find the length. And then access the last thing. Or just do the loop, then do something with a_list[-1]. Fancy way, use for-else - you can google it. But then again, really, there is nothing wrong with your code.
You can use the else block of a for-loop:
>>> for i in [1, 2, 3, 4, 5]:
... print(i)
... else:
... print(i**2)
...
1
2
3
4
5
25
>>>
As you can see, an operation is performed on each element in the list but the last one undergoes an extra operation.
Note too that the else block will only be run if the loop exits normally without encountering a break statement. This behavior seems proper because, if a break statement was encountered, then the loop was explicitly exited and we are done with the list.
You can use this:
a_list[-1]
to access last element
I would certainly prefer the second version of the two you present; index could cause problems if there are duplicates in the list and is an O(n) operation on every iteration, whereas len is O(1).
Generally, though, as you want to do something additional (not different) to the last item, I would just make it a separate step after the for loop:
for item in lst:
# do something to every element
# do something to lst[-1]
This will work even if there is a break (unlike using else) and affects the last item in the list not the last item iterated over - this may or may not be desired behaviour.
Consider:
li[:]=[do_somthing(item) for item in li] # something to every item in place
li[-1]=something_extra(li[-1]) # additional to last item
vs
for i, item in enumerate(li):
li[i]=do_somthing(item)
if i==len(li)-1:
li[i]=something_extra(item)
If you time these, you can see this is the fastest way:
def do_something(e):
return e*2
def something_extra(e):
return e/2
def f1(li):
for i, item in enumerate(li):
li[i]=do_something(item)
if i==len(li)-1:
li[i]=something_extra(item)
def f2(li):
li[:]=[do_something(item) for item in li]
li[-1]=something_extra(li[-1])
def f3(li):
for i, item in enumerate(li):
li[i]=do_something(item)
li[i]=something_extra(item)
if __name__ == '__main__':
import timeit
for f in (f1,f2,f3):
t=timeit.timeit("f(range(1000))",
setup="from __main__ import f,do_something,something_extra",
number=10000)
print '{}: {:6.3} seconds'.format(f.__name__, t)
On my (iMac) machine:
f1: 2.95 seconds
f2: 1.45 seconds
f3: 1.97 seconds

for-in loop's upper limit changing in each loop

How can I update the upper limit of a loop in each iteration? In the following code, List is shortened in each loop. However, the lenList in the for, in loop is not, even though I defined lenList as global. Any ideas how to solve this? (I'm using Python 2.sthg)
Thanks!
def similarity(List):
import difflib
lenList = len(List)
for i in range(1,lenList):
import numpy as np
global lenList
a = List[i]
idx = [difflib.SequenceMatcher(None, a, x).ratio() for x in List]
z = idx > .9
del List[z]
lenList = len(List)
X = ['jim','jimmy','luke','john','jake','matt','steve','tj','pat','chad','don']
similarity(X)
Looping over indices is bad practice in python. You may be able to accomplish what you want like this though (edited for comments):
def similarity(alist):
position = 0
while position < len(alist):
item = alist[position]
position += 1
# code here that modifies alist
A list will evaluate True if it has any entries, or False when it is empty. In this way you can consume a list that may grow during the manipulation of its items.
Additionally, if you absolutely have to have indices, you can get those as well:
for idx, item in enumerate(alist):
# code here, where items are actual list entries, and
# idx is the 0-based index of the item in the list.
In ... 3.x (I believe) you can even pass an optional parameter to enumerate to control the starting value of idx.
The issue here is that range() is only evaluated once at the start of the loop and produces a range generator (or list in 2.x) at that time. You can't then change the range. Not to mention that numbers and immutable, so you are assigning a new value to lenList, but that wouldn't affect any uses of it.
The best solution is to change the way your algorithm works not to rely on this behaviour.
The range is an object which is constructed before the first iteration of your loop, so you are iterating over the values in that object. You would instead need to use a while loop, although as Lattyware and g.d.d.c point out, it would not be very Pythonic.
What you are effectively looping on in the above code is a list which got generated in the first iteration itself.
You could have as well written the above as
li = range(1,lenList)
for i in li:
... your code ...
Changing lenList after li has been created has no effect on li
This problem will become quite a lot easier with one small modification to how your function works: instead of removing similar items from the existing list, create and return a new one with those items omitted.
For the specific case of just removing similarities to the first item, this simplifies down quite a bit, and removes the need to involve Numpy's fancy indexing (which you weren't actually using anyway, because of a missing call to np.array):
import difflib
def similarity(lst):
a = lst[0]
return [a] + \
[x for x in lst[1:] if difflib.SequenceMatcher(None, a, x).ratio() > .9]
From this basis, repeating it for every item in the list can be done recursively - you need to pass the list comprehension at the end back into similarity, and deal with receiving an empty list:
def similarity(lst):
if not lst:
return []
a = lst[0]
return [a] + similarity(
[x for x in lst[1:] if difflib.SequenceMatcher(None, a, x).ratio() > .9])
Also note that importing inside a function, and naming a variable list (shadowing the built-in list) are both practices worth avoiding, since they can make your code harder to follow.

How to use counter in for loop python

my_date_list = ['01', '02', '03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31']
str_date_list=[]
for item in my_date_list:
str_date_list.append(item+'-'+'05' + '-' +'09')
counter= 0
i = iter(range(31))
for item in i:
daily_user_status_list=[]
print counter
val_time1 = str_date_list[counter]
val_time2 = str_date_list[counter + 1]
counter =counter + 1
I am getting code error while doing counter = counter + 1.
Basically, I need to different time from my str_date_list each time.
but counter = counter +1 give me code error.
Is there any other way of doing it?
The counter is getting out of step with the sequences you're iterating over. But more than that, the counter is totally unnecessary.
You've got several manual iterations of things that could be automated, and they're causing you to trip over. Especially, you hardly ever need to manually track a counter while iterating; Python's sequence types know how to iterate themselves.
Here's my re-write of the intent of the above code (in the interactive interpreter to show it working):
>>> dates = ["%(day)02d-05-09" % vars() for day in range(1, 31+1)]
>>> date_ranges = zip(dates[:-1], dates[1:])
>>> for (date_begin, date_end) in date_ranges:
... print (date_begin, date_end)
...
('01-05-09', '02-05-09')
('02-05-09', '03-05-09')
('03-05-09', '04-05-09')
…
('28-05-09', '29-05-09')
('29-05-09', '30-05-09')
('30-05-09', '31-05-09')
Just for kicks, here's the super-compact Pythonic way to write this:
from itertools import izip, islice
str_date_list = ['%02d-05-09' % i for i in xrange(1, 32)]
for val_time1, val_time2 in izip(islice(str_date_list, 0, None), islice(str_date_list, 1, None)):
daily_user_status_list = [ <whatever goes here> ]
# more code...
The error you're seeing is because you're indexing out of range on the str_date_list list, not because you're incrementing the variable.
Compare the largest value of counter that the loop prints (30) to the length of the list (len(str_date_list)). Since indexing starts at 0, the largest index into a list of length n is n - 1.
you do not need to create a iterator for going thru 0-31
you can use enumerate e.g.
for i, sdate in enumerate(str_date_list):
print i, sdate
if you are using iter isn't item and counter same?
You don't need to duplicate the loop iteration variable and the counter:
my_date_list = ['01', '02', '03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31']
str_date_list=[]
for item in my_date_list:
str_date_list.append(item+'-'+'05' + '-' +'09')
for i in xrange(len(my_date_list)-1):
daily_user_status_list=[]
print i
val_time1 = str_date_list[i]
val_time2 = str_date_list[i + 1]
counter += 1
but that isn't the problem. What's the error? Indentation error maybe?
Better written:
str_date_list=[]
for n in xrange(1,32):
str_date_list.append(str(n).zfill(2)+'-'+'05' + '-' +'09')
for i in xrange(len(str_date_list)):
daily_user_status_list=[]
print i
val_time1 = str_date_list[i]
val_time2 = str_date_list[i + 1]
xrange gives us a (quite performing) iterator over natural numbers given bounds.
we use zfill to make sure there is a leading zero instead of writing them all explicitly
it's important to avoid iterating out of the array bounds!

Categories