What does this Python 3 function do? - python

Can anybody help me to find out how this function works?
def get_matched_birthdays(birthdays):
if len(birthdays) == len(set(birthdays)):
return None
for a, birthdayA in enumerate(birthdays):
for b, birthdayB in enumerate(birthdays[a+1:]):
if birthdayA == birthdayB:
return birthdayA
Here is birthdays: https://ghostbin.com/paste/4tM0V
I am understanding the first two lines but what's happening inside those two for loops?

The function basically loops through the "birthdays" list and checks to see if an element appears further down the list and returns the value every time it sees it down the line.
First for loop just loops through the "birthdays" list and gives each element an index which is stored in variable "a".
for a, birthdayA in enumerate(birthdays):
The second loop takes a subset of "birthdays" list and loops through it. The subset starts at the index "a+1" and ends at the end of the "birthdays" list.
for b, birthdayB in enumerate(birthdays[a+1:]):
The function then returns the values where the items in "birthdays" list comes up again in the list.
if birthdayA == birthdayB:
return birthdayA
If you want a more efficient way of checking for dupes and returning the index of dupes perhaps use something like this:
>>> dupes_lst = []
>>> unique_set = set()
>>> lst = [1,2,3,1,1,4,2]
>>> for i, item in enumerate(lst):
... if item in unique_set:
... dupes_lst.append(i)
... else:
... unique_set.add(item)
More on enumerate at https://docs.python.org/3/library/functions.html :
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]

Related

Python: Tie breaker when sorting a list of tuples (max for first element, and 1st occurrence of the second)

This is the list I started with
names = ["Alice", "Beatrice", "Amarnae", "Zootrope"]
The last three items all have 4 vowels each
Created a sorted list based on the count of vowels. Got the one shown below
mylist = [(3, 'Alice'), (4, 'Amarnae'), (4, 'Beatrice'), (4, 'Zootrope')]
I need to return/find the 1st occurrence of the max quantity
print(max(mylist))
which returns
(4, 'Zootrope')
Ask: I need to return as that is the first occurrence of the name in the original list
(4, 'Beatrice')
I have tried a few different things (incl. mylist.sort(key = lambda x: (x[0], x[1]) .. but doesn't seem to work.
Your mylist is not created with the original item order retained in the first place, hence the issue.
You can use a list comprehension like this to create mylist:
mylist = [(sum(c in 'aeiou' for c in name.lower()), name) for name in names]
which results in the following value for mylist, retaining the original item order:
[(3, 'Alice'), (4, 'Beatrice'), (4, 'Amarnae'), (4, 'Zootrope')]
Since max returns the first item with the maximum value, you can then simply use max with a key function that returns just the first item in the tuple for comparison, since you don't want the lexicographical order of the second item to be taken into account:
max(mylist, key=lambda t: t[0])
which returns:
(4, 'Beatrice')
Demo: https://replit.com/#blhsing/SwelteringFlakyDividend

How does this python enumerate script work and what makes it so fast? [duplicate]

What does for row_number, row in enumerate(cursor): do in Python?
What does enumerate mean in this context?
The enumerate() function adds a counter to an iterable.
So for each element in cursor, a tuple is produced with (counter, element); the for loop binds that to row_number and row, respectively.
Demo:
>>> elements = ('foo', 'bar', 'baz')
>>> for elem in elements:
... print elem
...
foo
bar
baz
>>> for count, elem in enumerate(elements):
... print count, elem
...
0 foo
1 bar
2 baz
By default, enumerate() starts counting at 0 but if you give it a second integer argument, it'll start from that number instead:
>>> for count, elem in enumerate(elements, 42):
... print count, elem
...
42 foo
43 bar
44 baz
If you were to re-implement enumerate() in Python, here are two ways of achieving that; one using itertools.count() to do the counting, the other manually counting in a generator function:
from itertools import count
def enumerate(it, start=0):
# return an iterator that adds a counter to each element of it
return zip(count(start), it)
and
def enumerate(it, start=0):
count = start
for elem in it:
yield (count, elem)
count += 1
The actual implementation in C is closer to the latter, with optimisations to reuse a single tuple object for the common for i, ... unpacking case and using a standard C integer value for the counter until the counter becomes too large to avoid using a Python integer object (which is unbounded).
It's a builtin function that returns an object that can be iterated over. See the documentation.
In short, it loops over the elements of an iterable (like a list), as well as an index number, combined in a tuple:
for item in enumerate(["a", "b", "c"]):
print item
prints
(0, "a")
(1, "b")
(2, "c")
It's helpful if you want to loop over a sequence (or other iterable thing), and also want to have an index counter available. If you want the counter to start from some other value (usually 1), you can give that as second argument to enumerate.
I am reading a book (Effective Python) by Brett Slatkin and he shows another way to iterate over a list and also know the index of the current item in the list but he suggests that it is better not to use it and to use enumerate instead.
I know you asked what enumerate means, but when I understood the following, I also understood how enumerate makes iterating over a list while knowing the index of the current item easier (and more readable).
list_of_letters = ['a', 'b', 'c']
for i in range(len(list_of_letters)):
letter = list_of_letters[i]
print (i, letter)
The output is:
0 a
1 b
2 c
I also used to do something, even sillier before I read about the enumerate function.
i = 0
for n in list_of_letters:
print (i, n)
i += 1
It produces the same output.
But with enumerate I just have to write:
list_of_letters = ['a', 'b', 'c']
for i, letter in enumerate(list_of_letters):
print (i, letter)
As other users have mentioned, enumerate is a generator that adds an incremental index next to each item of an iterable.
So if you have a list say l = ["test_1", "test_2", "test_3"], the list(enumerate(l)) will give you something like this: [(0, 'test_1'), (1, 'test_2'), (2, 'test_3')].
Now, when this is useful? A possible use case is when you want to iterate over items, and you want to skip a specific item that you only know its index in the list but not its value (because its value is not known at the time).
for index, value in enumerate(joint_values):
if index == 3:
continue
# Do something with the other `value`
So your code reads better because you could also do a regular for loop with range but then to access the items you need to index them (i.e., joint_values[i]).
Although another user mentioned an implementation of enumerate using zip, I think a more pure (but slightly more complex) way without using itertools is the following:
def enumerate(l, start=0):
return zip(range(start, len(l) + start), l)
Example:
l = ["test_1", "test_2", "test_3"]
enumerate(l)
enumerate(l, 10)
Output:
[(0, 'test_1'), (1, 'test_2'), (2, 'test_3')]
[(10, 'test_1'), (11, 'test_2'), (12, 'test_3')]
As mentioned in the comments, this approach with range will not work with arbitrary iterables as the original enumerate function does.
The enumerate function works as follows:
doc = """I like movie. But I don't like the cast. The story is very nice"""
doc1 = doc.split('.')
for i in enumerate(doc1):
print(i)
The output is
(0, 'I like movie')
(1, " But I don't like the cast")
(2, ' The story is very nice')
I am assuming that you know how to iterate over elements in some list:
for el in my_list:
# do something
Now sometimes not only you need to iterate over the elements, but also you need the index for each iteration. One way to do it is:
i = 0
for el in my_list:
# do somethings, and use value of "i" somehow
i += 1
However, a nicer way is to user the function "enumerate". What enumerate does is that it receives a list, and it returns a list-like object (an iterable that you can iterate over) but each element of this new list itself contains 2 elements: the index and the value from that original input list:
So if you have
arr = ['a', 'b', 'c']
Then the command
enumerate(arr)
returns something like:
[(0,'a'), (1,'b'), (2,'c')]
Now If you iterate over a list (or an iterable) where each element itself has 2 sub-elements, you can capture both of those sub-elements in the for loop like below:
for index, value in enumerate(arr):
print(index,value)
which would print out the sub-elements of the output of enumerate.
And in general you can basically "unpack" multiple items from list into multiple variables like below:
idx,value = (2,'c')
print(idx)
print(value)
which would print
2
c
This is the kind of assignment happening in each iteration of that loop with enumerate(arr) as iterable.
the enumerate function calculates an elements index and the elements value at the same time. i believe the following code will help explain what is going on.
for i,item in enumerate(initial_config):
print(f'index{i} value{item}')

printing out tuples from function

Im making this function that prints the index number with the item from the string but I need it to print out as tuples, for example
[(0, 'dog'), (1, 'pig'), (2, 'cow')]
how would I go about doing this? it just prints each out on one line so far.
this is my code so far.
def my_enumerate(items):
"""return a list of tuples where item is the i'th item """
for item in items:
index = items.index(item)
print(item, index)
my_enumerate(['dog', 'pig', 'cow'])
thank you
def my_enumerate(items):
"""return a list of tuples where item is the i'th item """
l = []
for index in range(len(items)):
t = (index, items[index])
l.append(t)
return l
res = my_enumerate(['x', 'x', 'x'])
print(res)
I would suggest:
def my_enumerate(items):
print([(i, animal) for i, animal in enumerate(items)])
the reason you are printing them one by one is that you are printing them one by one inside the loop, to get all of them at once you will need to create a list and print that at the end of the loop. there are several ways to go about this and m0nte-cr1st0 gave you the most legible one, ill give you something a little shorter:
def my_enumerate(items):
return list(zip(range(len(items)), items))
zip lets you smoosh together 2 lists pairwise
this solution of course assumes you cant just use the built in enumerate
In expression
print(list(enumerate(items)))
part enumerate(['dog', 'pig', 'cow']) will create an iterator over the list of tuples [(0, 'dog'), (1, 'pig'), (2, 'cow')] you want to print , so you can just create a list with this iterator and print it.

Searching List of Tuples by nth element in Python

So I have a list of tuples, with 3 elements each. Both the second and third elements are ints. For a value n, I need to return all the tuples that contain n as either the second or third element.
I'm not entirely sure how to do this, unfortunately. I'm sure it's not too complicated, but although there are some similar questions, I can't find any for this exact problem. Does anyone know how to go about this?
Thanks
You should be able to do this with a simple list comprehension. Something like:
[t for t in list_of_tuples if t[1] == n or t[2] == n]
Use a list comprehension with a simple if condition:
>>> lis=[('a',1,2),('b',2,2),('c',3,3),('d',3,1)]
>>> n=1
>>> [x for x in lis if n in x[1:3]] #[1:3] returns a sublist containing
# 2 & 3 element of each tuple
[('a', 1, 2), ('d', 3, 1)]
blist = [tup for tup in alist if n in tup[1:]]
The line above uses a list comprehension, and is equivalent to:
blist = []
for tup in alist:
if n in tup[1:]:
blist.append(tup)
tup[1:] returns a new tuple, consisting of the second and third items in the three item tuple tup.
In hindsight James Henstridge's example seems preferable, because t[1] == n or t[2] == n uses the existing tuple.

iterating through a list with an if statement

I have a list that I am looping through with a "for" loop and am running each value in the list through an if statement. My problem is that I am trying to only have the program do something if all the values in the list pass the if statement and if one doesn't pass, I want it to move along to the next value in the list. Currently it is returning a value if a single item in the list passes the if statement. Any ideas to get me pointed in the right direction?
Python gives you loads of options to deal with such a situation. If you have example code we could narrow that down for you.
One option you could look at is the all operator:
>>> all([1,2,3,4])
True
>>> all([1,2,3,False])
False
You could also check for the length of the filtered list:
>>> input = [1,2,3,4]
>>> tested = [i for i in input if i > 2]
>>> len(tested) == len(input)
False
If you are using a for construct you can exit the loop early if you come across negative test:
>>> def test(input):
... for i in input:
... if not i > 2:
... return False
... do_something_with_i(i)
... return True
The test function above will return False on the first value that's 2 or lower, for example, while it'll return True only if all values were larger than 2.
Maybe you could try with an for ... else statement.
for item in my_list:
if not my_condition(item):
break # one item didn't complete the condition, get out of this loop
else:
# here we are if all items respect the condition
do_the_stuff(my_list)
You need to loop through your whole list and check the condition before trying to do anything else with the data, so you need two loops (or use some built in that does the loop for you, like all()). From this codepad with nothing too fancy, http://codepad.org/pKfT4Gdc
def my_condition(v):
return v % 2 == 0
def do_if_pass(l):
list_okay = True
for v in l:
if not my_condition(v):
list_okay = False
if list_okay:
print 'everything in list is okay, including',
for v in l:
print v,
print
else:
print 'not okay'
do_if_pass([1,2,3])
do_if_pass([2,4,6])
You must always be careful if you're deleting items from your list while you're trying to iterate through it.
If you're not deleting then does this help:
>>> yourlist=list("abcdefg")
>>> value_position_pairs=zip(yourlist,range(len(yourlist)))
>>> value_position_pairs
[('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5), ('g', 6)]
>>> filterfunc=lambda x:x[0] in "adg"
>>> value_position_pairs=filter(filterfunc,value_position_pairs)
>>> value_position_pairs
[('a', 0), ('d', 3), ('g', 6)]
>>> yourlist[6]
'g'
now if value_position_pairs is empty you're done. If not you can increase i by one to go to the next value or iterate through the failed values using their position in the array.

Categories

Resources