I am referring to this this specific answer Making nested lists same length. Since I don't have the permissions to comment yet and answering with a question to that topic would violate the rules, I am asking a new question.
I don't fully understand the answer.
In my understanding the iterator row in the for-loop is usually an integer value which iterates over each element in myList. So how is it possible to use len(row) as part of the condition since it is just an integer? Is there something I am missing?
I have tried to apply this solution to my code but as expected I receive an error saying
TypeError: object of type 'int' has no len()
args = ("object of type 'int' has no len()",)
with_traceback = <built-in method with_traceback of TypeError object>
referring to this line
row.extend(['null'*(len(maxSS7) - len(row))])
Further I don't understand the use of .extend with row which is the iterator and not a list.
Here is the relevant part from the answer.
maxLen = max(map(len, myList))
for row in myList:
if len(row) < maxLen:
row.extend(...)
A brief walkthrough would be greatly appreciated.
Or maybe there is a better way to adjust the lengths of all nested list to same length.
Ok, lets go through it line by line. Personally I don't think map is very idiomatic in Python so I would write this:
maxLen = max(map(len, myList))
As a generator expresion:
max_len = max(len(item) for item in my_list)
The second version is almost plain English: let max_len be the maximum value among the length of each item in my_list.
The best way to understand something in Python is just fire up the REPL and try it. So if you have my_list as a list of lists:
my_list = [[1], [1, 2], [1, 2, 3]]
The above will get you the length of the largest item: 3
Now you want to make every item the same size. How can you do that? One way is to append None items to it. For each item in the list, you test if the length of the item is smaller then the largest item in the list and it is almost plain English:
for item in list: # for each item in the list
while len(item) < max_len: # while the item length is smaller than 3
item.append(None) # append None to the item
You may want to do a bit of premature optimization and call extend once instead of calling append several times because you think performance will be better this way (but you can't really tell it unless you have profiled both solutions):
for item in list:
if len(item) < max_len:
item.extend([None] * (max_len - len(item)))
Now what is going on here? In Python, list + list concatenates two copies of list, and list * 3 is the same as list + list + list. So in the first iteration of the for loop, item is [1], len(item) is 1 and max_len - len(item) is 3 - 1. Finally, [None] * 2 is [None, None] so after the call to extend the first item will be [1, None, None]. Same thing for the second item, its length is 2, 3 minus 2 is one and it will end up as [1, 2, None]. The 3rd item has the same length as max_len (3) so the if condition is false. The result will be:
[[1, None, None], [1, 2, None], [1, 2, 3]]
All lists in the list of lists now have the same size, 3. For the sake of completion, for such a small list the extend version is less than 1 microsecond faster than the append one so barely worth the trouble (1.64 µs versus 1.7 µs in Python 3.6 running on my Mac).
for row in myList:
You created a variable called row which refers to an item within myList.
row is an integer.
You can get len of a list. Not an integer.
You messed up making your list. Troubleshoot it by printing the myList and seeing what the structure looks like so you know what you're iterating through cause it's probably not a 2d list.
Related
I've had a look through the forums and can't find anything to do with multiplying all elements in an array recursively.
I've created the following code that almost does what I want. The goal is to use no loops and only recursion.
Here's the code:
def multAll(k,A):
multAllAux(k,A)
return A[:]
def multAllAux(k,A):
B = [0]
if A == []:
return 0
else:
B[0] = (A[0] * k)
B.append(multAllAux(k,A[1:]))
return B
print(multAllAux(10, [5,12,31,7,25] ))
The current output is:
[50, [120, [310, [70, [250, 0]]]]]
However, it should be:
[50,120,310,70,250]
I know I am close, but I am at a complete loss at this point. The restrictions of no loops and solely recursion has left me boggled!
Your multAllAux function returns a list. If you append a list to another list, you get this nested list kind of structure that you are getting right now.
If you instead use the "extend" function; it will work as expected.
>>> a = [1, 2, 3]
>>> a.extend([4, 5])
>>> a
[1, 2, 3, 4, 5]
extend takes the elements from a second list and adds them to the first list, instead of adding the second list itself which is what append does!
Your function also returns a zero at the end of the list, which you don't need. You can try this:
def mult(k, A: list) -> list:
return [k * A[0]] + mult(k, A[1:]) if A else []
The problem is here:
B.append(multAllAux(k,A[1:])))
What .append(..) does is it takes the argument, considers it as a single element and adds that element to the end of the list. What you want is to concatenate to the list (ie the item being added should be seen as a list of elements rather than one single element).
You can say something like: B = B + multAllAux(..) or just use +=
B += multAllAux(...)
BTW, if you wanted to multiply a single number to a list, there is a very similar construct: map(..). Note that this behaves slightly differently depending on whether you're using Py2 or Py3.
print(map(lambda x: x * 10, [5,12,31,7,25]))
I have a list:
input = ['a','b','c','a','b','d','e','d','g','g']
I want index of all elements except duplicate in a list.
output = [0,1,2,5,6,8]
You should iterate over the enumerated list and add each element to a set of "seen" elements and add the index to the output list if the element hasn't already been seen (is not in the "seen" set).
Oh, the name input overrides the built-in input() function, so I renamed it input_list.
output = []
seen = set()
for i,e in enumerate(input_list):
if e not in seen:
output.append(i)
seen.add(e)
which gives output as [0, 1, 2, 5, 6, 8].
why use a set?
You could be thinking, why use a set when you could do something like:
[i for i,e in enumerate(input_list) if input_list.index(e) == i]
which would work because .index returns you the index of the first element in a list with that value, so if you check the index of an element against this, you can assert that it is the first occurrence of that element and filter out those elements which aren't the first occurrences.
However, this is not as efficient as using a set, because list.index requires Python to iterate over the list until it finds the element (or doesn't). This operation is O(n) complexity and since we are calling it for every element in input_list, the whole solution would be O(n^2).
On the other hand, using a set, as in the first solution, yields an O(n) solution, because checking if an element is in a set is complexity O(1) (average case). This is due to how sets are implemented (they are like lists, but each element is stored at the index of its hash so you can just compute the hash of an element and see if there is an element there to check membership rather than iterating over it - note that this is a vague oversimplification but is the idea of them).
Thus, since each check for membership is O(1), and we do this for each element, we get an O(n) solution which is much better than an O(n^2) solution.
You could do a something like this, checking for counts (although this is computation-heavy):
indexes = []
for i, x in enumerate(inputlist):
if (inputlist.count(x) == 1
and x not in inputlist[:i]):
indexes.append(i)
This checks for the following:
if the item appears only once. If so, continue...
if the item hasn't appeared before in the list up till now. If so, add to the results list
In case you don't mind indexes of the last occurrences of duplicates instead and are using Python 3.6+, here's an alternative solution:
list(dict(map(reversed, enumerate(input))).values())
This returns:
[3, 4, 2, 7, 6, 9]
Here is a one-liner using zip and reversed
>>> input = ['a','b','c','a','b','d','e','d','g','g']
>>> sorted(dict(zip(reversed(input), range(len(input)-1, -1, -1))).values())
[0, 1, 2, 5, 6, 8]
This question is missing a pandas solution. 😉
>>> import pandas as pd
>>> inp = ['a','b','c','a','b','d','e','d','g','g']
>>>
>>> pd.DataFrame(list(enumerate(inp))).groupby(1).first()[0].tolist()
[0, 1, 2, 5, 6, 8]
Yet another version, using a side effect in a list comprehension.
>>> xs=['a','b','c','a','b','d','e','d','g','g']
>>> seen = set()
>>> [i for i, v in enumerate(xs) if v not in seen and not seen.add(v)]
[0, 1, 2, 5, 6, 8]
The list comprehension filters indices of values that have not been seen already.
The trick is that not seen.add(v) is always true because seen.add(v) returns None.
Because of short circuit evaluation, seen.add(v) is performed if and only if v is not in seen, adding new values to seen on the fly.
At the end, seen contains all the values of the input list.
>>> seen
{'a', 'c', 'g', 'b', 'd', 'e'}
Note: it is usually a bad idea to use side effects in list comprehension,
but you might see this trick sometimes.
I would like to add an item to a list in python, I want to add the item at an index which is greater than the size of the list. This should cause the list to grow automatically.
For example with a list of 3 items, I would like to insert element at index 6. Is this possible in python without first having to reinitialize the list?
It seems Python will merely append to the list if you try to insert an item at index 6 in my example.
You could write a function which, given a list, an index, and an element, inserts the element in the list before the index if the index is in range (in which case this is equivalent to the built-in insert method) or extends the list by enough Nones to fill it out before tacking it on the end:
>>> def put(xs,i,e):
n = len(xs)
if i <= n:
xs.insert(i,e)
else:
xs.extend([None]*(i-n-1))
xs.append(e)
>>> xs = [1,2,3]
>>> put(xs,6,10)
>>> xs
[1, 2, 3, None, None, 10]
>>>
>>> n = [1, 2, 3]
>>> for item in n:
... item *= 2
...
>>> print n
[1, 2, 3]
I expect the result of the above code to be [2, 4, 6], While obviously it's not.
Then I tried for i in range(n) as follows
>>> n = [1, 2, 3]
>>> for i in range(len(n)):
... n[i] *= 2
...
>>>
>>> n
[2, 4, 6]
This seems OK.
And my question is that, what's the essential difference between these two for loop method? What cause the unexpected result above?
If it helps, the first loop is equivalent to:
for i in range(len(n)):
item = n[i]
item *= 2
In other words, it first binds item to the i-th element of the list, and then rebinds it to a new object whose value is double that of the i-th element. It does not change any of the list's elements.
A good way to implement this loop is using a list comprehension:
n = [item * 2 for item in n]
You can't modify the object that represents the current iteration.
Well, actually, you can, but it won't change the object that is held in the list.
what's the essential difference between these two for loop method?
You iterate over objects in the list in the first example (and try to modify that said object directly - it doesn't change the list's element itself).
And you iterate over the list of integers in the second example (and actually modify the given list's element, so you modify the list content).
item is simply a local name. It is originally assigned by the for loop to point to the current element, but if you reassign it to point to something else, that has no effect on the original.
But if you use an index to reference an element in the original list, you can mutate that list to contain different values.
There's no assignment in for item in lst. You're operating on the object itself, which is immutable, so it just creates a new object for you when you do the *= call, assigns it to item, then throws it away on the next iteration of the loop.
When you do for i in range(len(lst)) you're assigning the new object to the ith element of lst.
I have a following problem while trying to do some nodal analysis:
For example:
my_list=[[1,2,3,1],[2,3,1,2],[3,2,1,3]]
I want to write a function that treats the element_list inside my_list in a following way:
-The number of occurrence of certain element inside the list of my_list is not important and, as long as the unique elements inside the list are same, they are identical.
Find the identical loop based on the above premises and only keep the
first one and ignore other identical lists of my_list while preserving
the order.
Thus, in above example the function should return just the first list which is [1,2,3,1] because all the lists inside my_list are equal based on above premises.
I wrote a function in python to do this but I think it can be shortened and I am not sure if this is an efficient way to do it. Here is my code:
def _remove_duplicate_loops(duplicate_loop):
loops=[]
for i in range(len(duplicate_loop)):
unique_el_list=[]
for j in range(len(duplicate_loop[i])):
if (duplicate_loop[i][j] not in unique_el_list):
unique_el_list.append(duplicate_loop[i][j])
loops.append(unique_el_list[:])
loops_set=[set(x) for x in loops]
unique_loop_dict={}
for k in range(len(loops_set)):
if (loops_set[k] not in list(unique_loop_dict.values())):
unique_loop_dict[k]=loops_set[k]
unique_loop_pos=list(unique_loop_dict.keys())
unique_loops=[]
for l in range(len(unique_loop_pos)):
unique_loops.append(duplicate_loop[l])
return unique_loops
from collections import OrderedDict
my_list = [[1, 2, 3, 1], [2, 3, 1, 2], [3, 2, 1, 3]]
seen_combos = OrderedDict()
for sublist in my_list:
unique_elements = frozenset(sublist)
if unique_elements not in seen_combos:
seen_combos[unique_elements] = sublist
my_list = seen_combos.values()
you could do it in a fairly straightforward way using dictionaries. but you'll need to use frozenset instead of set, as sets are mutable and therefore not hashable.
def _remove_duplicate_lists(duplicate_loop):
dupdict = OrderedDict((frozenset(x), x) for x in reversed(duplicate_loop))
return reversed(dupdict.values())
should do it. Note the double reversed() because normally the last item is the one that is preserved, where you want the first, and the double reverses accomplish that.
edit: correction, yes, per Steven's answer, it must be an OrderedDict(), or the values returned will not be correct. His version might be slightly faster too..
edit again: You need an ordered dict if the order of the lists is important. Say your list is
[[1,2,3,4], [4,3,2,1], [5,6,7,8]]
The ordered dict version will ALWAYS return
[[1,2,3,4], [5,6,7,8]]
However, the regular dict version may return the above, or may return
[[5,6,7,8], [1,2,3,4]]
If you don't care, a non-ordered dict version may be faster/use less memory.