repeatedly calling a method in list comprehension - python

Let's consider a list called 'my_list` whose contents are as follows:
['10', '100', '1,000', '10,000', 100,000']
I want to verify that my_list is a list of stringified integers, which are multiples of 10 and are sorted in ascending order, so here's what i do
int_list = [int(each_int.replace(',', '')) for each_int in my_list]
boolean = all([int_list[idx] == int_list[idx-1]*10 for idx in xrange(1, len(int_list))])
my question is will the len() be called for every iteration? what is better practice in such cases?
assign the length to a variable and use that instead of the len() itself in the list comprehension
it doesn't matter, len() is executed only once for all the iterations
if 2. , is it applicable to cases when say, I am iterating through the values/ keys of a dictionary of lists (or maybe just a dictionary)
ex: d_list = [set(value) for value in my_dict.values()]

You can do it as:
my_list = ['10', '100', '1,000', '10,000', '100,000']
int_list = [int(each_int.replace(',', '')) for each_int in my_list]
>>> print all(i*10==j for i,j in zip(int_list, int_list[1:]))
True
This will avoid any unnecessary repetitions of calculations and is also my faster since it is doing comparison by division. I have also replaced all([...]) with all(...) since all can handle generators and will save from having to create a temporary list.

Related

Iterating through and selecting nth element in multiple lists of lists - python 2

I have multiple lists of lists. I need to get the 2nd element of each inner list and make multiple new lists composed of these 2nd elements.
Sample data:
for item in list:
x = fpp.table
print x
[['hello', 'mum'],['goodbye', 'dad']]
[['3', '6', '9'], ['2', '4', '6']]
So with this data I want to turn it into the two following lists:
['mum','dad']
['6','4']
The accepted answer is correct. However, the most Pythonic (IMO) way to do that is (BTW, you should avoid to name your variable list since this is a type in Python) to use a list comprehension:
[elt[1] for elt in my_list]
If you want to get the second element of each list only when the list has at least two elements (otherwise, the previous code would crash), you can add a condition to the list comprehension:
[elt[1] for elt in my_list if len(elt) >= 2]
Lets try making a function like this:
def secondElement(list):
secondL = []
for item in list:
secondL.append(item[1])
print (secondL)
This should do the job for getting the 2nd element of the every integrated sub-list from the main list. Hope this is what you were looking for!

Include empty values in a list according to specific positions (Python)

I have the following list:
CompleteList=['00:00:00', '00:00:01', '00:00:02', '00:00:03',....,'23:59:59']
and I also have the following list:
IncompleteList=['00:00:00', '00:00:01', '00:00:03',....,'23:59:59']
As it can be seen the CompleteList has values that are missing in the IncompleteList, as for example value '00:00:02'.
I also have a third array:
MyList=['22', '33', '25',....,'13']
What I need is to include empty values in MyList in those position where IncompleteList has missing values in the following way:
MyList_result=['22', '33','','25',....,'13']
I have achieved this in the following way:
MyList_result=[]
for item in CompleteList:
if item in IncompleteList:
ind=IncompleteList.index(item)
v=MyList[ind]
MyList_result.append(v)
else:
v=''
MyList_result.append(v)
This works but it takes too long taking into account the size of the lists that I am working with. I really need to find a more efficient way of doing it. Any help will be appreciated.
The first intuitive approach would be to convert the IncompleteList to a set and get an iterator for MyList. Then it becomes a linear operation in iterating over CompleteList and spit out the next item from the MyList iterator if the elem from CompleteList is present in IncompleteList else as per your example an empty string
Sample Code
IncompleteList=['00:00:00', '00:00:01', '00:00:03','23:59:59']
IncompleteSet = set(IncompleteList)
MyList=['22', '33', '25','13']
CompleteList=['00:00:00', '00:00:01', '00:00:02', '00:00:03','23:59:59']
MyListIt = iter(MyList)
[next(MyListIt) if cl_elem in IncompleteSet else '' for cl_elem in CompleteList]
Sample Output
Out[100]: ['22', '33', '', '25', '13']
Alternatively you can zip both the IncompleteList and MyList and convert the paired list as a dictionary. Following which iterate over the CompleteList and spit out the corresponding value from the dictionary if the element is present else an empty string
MyDict = dict(zip(IncompleteList, MyList))
[MyDict.get(k, '') for k in CompleteList]
Out[108]: ['22', '33', '', '25', '13']
The bottleneck from your implementation is in two places:
You are checking for each item from the CompleteList in the IncompleteList at
if item in IncompleteList:
which in the worst case would scan the IncompleteList n number of times (if n is the number of elements in the CompleteList)
If the item is present you find the index of the item at
ind = IncompleteList.index(item)
which involves another scan of the IncompleteList
The first solution suggested by #Abhijit solves the second problem where you do not have to scan the list a second time to get the index. However the check for the presence of the item in the IncompleteList/IncompleteSet is still a bottleneck.
If we can assume sorted lists then the following solution will be faster although a little more complex:
MyList_result = []
incomplete_list_index = 0
incomplete_list_length = len(IncompleteList)
for item in CompleteList:
if incomplete_list_index < incomplete_list_length and IncompleteList[incomplete_list_index] == item:
MyList_result.append(MyList[incomplete_list_index])
incomplete_list_index += 1
else:
MyList_result.append('')
This involves just a single pass of the CompleteList (and no pre-processing to generate a Dict as the second solution suggested by #Abhijit).

Accessing elements of a list

I have a list of strings, and calling a function on each string which returns a string. The thing I want is to update the string in the list. How can I do that?
for i in list:
func(i)
The function func() returns a string. i want to update the list with this string. How can it be done?
If you need to update your list in place (not create a new list to replace it), you'll need to get indexes that corresponds to each item you get from your loop. The easiest way to do that is to use the built-in enumerate function:
for index, item in enumerate(lst):
lst[index] = func(item)
You can reconstruct the list with list comprehension like this
list_of_strings = [func(str_obj) for str_obj in list_of_strings]
Or, you can use the builtin map function like this
list_of_strings = map(func, list_of_strings)
Note : If you are using Python 3.x, then you need to convert the map object to a list, explicitly, like this
list_of_strings = list(map(func, list_of_strings))
Note 1: You don't have to worry about the old list and its memory. When you make the variable list_of_strings refer a new list by assigning to it, the reference count of the old list reduces by 1. And when the reference count drops to 0, it will be automatically garbage collected.
First, don't call your lists list (that's the built-in list constructor).
The most Pythonic way of doing what you want is a list comprehension:
lst = [func(i) for i in lst]
or you can create a new list:
lst2 = []
for i in lst:
lst2.append(func(i))
and you can even mutate the list in place
for n, i in enumerate(lst):
lst[n] = func(i)
Note: most programmers will be confused by calling the list item i in the loop above since i is normally used as a loop index counter, I'm just using it here for consistency.
You should get used to the first version though, it's much easier to understand when you come back to the code six months from now.
Later you might also want to use a generator...
g = (func(i) for i in lst)
lst = list(g)
You can use map() to do that.
map(func, list)

Python3: sorting a list of dictionary keys

I have a list of 760 files, from which I extract 2 lines of data, which are then stored in a dictionary:
output = {'file number':data, '1':somedatain1, '2':somedatain2, ... '760':somedatain760}
N.B.
The numbers are strings because they have been obtained by doing an os.listdir('.') in order to get a list of the filenames and splitting the string down. [I could convert this into an integer number (using int()) if needed]
The dictionary is then printed by creating a list of the keys and iterating:
keys = output.keys()
for x in keys:
print(x, '\t', output[x])
However the output is in a random order [because of the unordered nature of a dictionary, which is, I believe, an inherent property - although I don't know why this is] and it would be far more convenient if the output was in numerical order according to the file number. This, then throws up the question:
Given that my list of keys is either
1.keys = ['filename', '2', '555', '764' ... '10']
or, if i change the string of the file number to an integer:
2.keys = ['filename', 2, 555, 764 ... 10]
how do i sort my list of keys according to the numeric value of the file number if it is strings (as shown in 1. above), or if it is of mixed object types (i.e. 1 string and 760 integers as shown in 2 above)?
You can give the sorted() function a key:
sorted(output, key=lambda k: int(k) if k.isdigit() else float('-inf'))
This will sort strings before numbers, however. Note that there is no need to call dict.keys(); iteration over a dictionary already yields a sequence of keys, just call sorted() directly on the dictionary.
Python 3 does not define ordering for strings when compared with numbers, so for any key that is not a digit, float('-inf') (negative infinity) is returned instead to at least put those keys at the start of the ordering.
Demo:
>>> sorted(keys, key=lambda k: int(k) if k.isdigit() else float('-inf'))
['filename', '2', '10', '555', '764']
Just add your list to another variable and then following statement you get correct output:
listofdict = [{'key': value1,'key': value2,.......}]
output = listofdict[::-1]
print(output)

difference between 2 pieces Python code

I'm doing an exercise as following:
# B. front_x
# Given a list of strings, return a list with the strings
# in sorted order, except group all the strings that begin with 'x' first.
# e.g. ['mix', 'xyz', 'apple', 'xanadu', 'aardvark'] yields
# ['xanadu', 'xyz', 'aardvark', 'apple', 'mix']
# Hint: this can be done by making 2 lists and sorting each of them
# before combining them.
sample solution:
def front_x(words):
listX = []
listO = []
for w in words:
if w.startswith('x'):
listX.append(w)
else:
listO.append(w)
listX.sort()
listO.sort()
return listX + listO
my solution:
def front_x(words):
listX = []
for w in words:
if w.startswith('x'):
listX.append(w)
words.remove(w)
listX.sort()
words.sort()
return listX + words
as I tested my solution, the result is a little weird. Here is the source code with my solution: http://dl.dropbox.com/u/559353/list1.py. You might want to try it out.
The problem is that you loop over the list and remove elements from it (modifying it):
for w in words:
if w.startswith('x'):
listX.append(w)
words.remove(w)
Example:
>>> a = range(5)
>>> for i in a:
... a.remove(i)
...
>>> a
[1, 3]
This code works as follows:
Get first element, remove it.
Move to the next element. But it is not 1 anymore because we removed 0 previously and thus 1 become the new first element. The next element is therefore 2 and 1 is skipped.
Same for 3 and 4.
Two main differences:
Removing an element from a list inside loop where the list is being iterated doesn't quite work in Python. If you were using Java you would get an exception saying that you are modifying a collection that is being iterated. Python doesn't shout this error apparently. #Felix_Kling explains it quite well in his answer.
Also you are modifying the input parameter words. So the caller of your function front_x will see words modified after the execution of the function. This behaviour, unless is explicitly expected, is better to be avoided. Imagine that your program is doing something else with words. Keeping two lists as in the sample solution is a better approach.
Altering the list you're iterating over results in undefined behaviour. That's why the sample solution creates two new lists instead of deleting from the source list.
for w in words:
if w.startswith('x'):
listX.append(w)
words.remove(w) # Problem here!
See this question for a discussion on this matter. It basically boils down to list iterators iterating through the indexes of the list without going back and checking for modifications (which would be expensive!).
If you want to avoid creating a second list, you will have to perform two iterations. One to iterate over words to create listX and another to iterate over listX deleting from words.
That hint is misleading and unnecessary, you can do this without sorting and combining two lists independently:
>>> items = ['mix', 'xyz', 'apple', 'xanadu', 'aardvark']
>>> sorted(items, key=lambda item: (item[0]!='x', item))
['xanadu', 'xyz', 'aardvark', 'apple', 'mix']
The built-in sorted() function takes an option key argument that tells it what to sort by. In this case, you want to create a tuples like (False, 'xanadu') or (True, 'apple') for each element of the original list, which you can do with a lambda.

Categories

Resources