'int' object is not iterable and the difference between two codes? - python

I want to figure the length of the shortest word in a string
Here is my code:
def find_short(s):
for x in s.split():
return min (len(x))
The correct one:
def find_short(s):
return min(len(x) for x in s.split())
so what's the difference between my code and the correct one? which piece of code is not iterable?

min() takes a sequence, and returns the smallest item in that sequence.
len() takes a sequence, and returns a single length (the length of that sequence).
Your first code calls min() on each length... which doesn't make sense, because min() expects a sequence as input.

maybe wrong but my guess is that you are returning the minimum length of x for every x...

Input: what is your question
def find_short(s):
for x in s.split(): # here s.split() generates a list [what, is, your, question]
return min(len(x)) # you will pass the length of the first element because you're only passing length of one word len(what) to min and immediately trying to return.
you need to pass iterable items to min function not int type
Where as here
def find_short(s):
return min(len(x) for x in s.split())
#here there are three nested operations:
#1. split the string into list s.split() [what, is, your, question]
#2. loop the list and find the length of each word by len(x) and put it into a tuple compression (4, 2, 4, 8)
#3. pass that tuple compression to the min function min((4, 2, 4, 8))
# and lastly return the smallest element from tuple

def find_short(s):
for x in s.split():
return min (len(x))
Here, x contains individul words. For every iteration, you will get a new word in x. len(x) returns an int. Calling min on an int will give you the error:
'int' object is not iterable
The verbose solution should be like below:
def find_short(s):
min_length = s[0]
for x in s.split():
if len(x) < min_length:
min_length = len(x)
return min_length
Now, look at the correct version.
def find_short(s):
return min(len(x) for x in s.split())
Here, this len(x) for x in s.split() part would form a generator, where each element is int(length of individual words). And, min() is called on the generator, not on individual ints. So, this works perfectly fine.
In short
>> min(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
>>
>>min([1])
>> 1

Related

how to write a recursive function that sums the integers in a tree?

Write a recursive function TreeSum(T) to add up all the integers in a tree. My test case is
TreeSum(set)
48
where
set=[[[[2,1],[3,7]],[1,2]],[[0,6],[[[3,2],[1,1]],[9,10]]]]
This is the code I have so far:
def TreeSum(T):
if len(T)==0:
return 0
else:
return T[0] + TreeSum(T[1:])
I am getting an error from the return line ("can only concatenate list (not "int") to list"). How can I fix this?
Error:
----> 5 return T[0] + TreeSum(T[1:])
6
7 TreeSum(maple)
TypeError: can only concatenate list (not "int") to list
#j1-lee had the correct approach, but this is limited in terms of span of the sublists (silently ignoring the items with a position > 1, or failing if one of the first two elements is an integer). You need to apply the recursive function on all sub-elements. One way is to use map
NB. I slightly changed the input to add two more elements to the first sublist
tree = [[[[2,1],1,[3,7],[1]],[1,2]],[[0,6],[[[3,2],[1,1]],[9,10]]]]
def treesum(x):
if not isinstance(x, list):
return x
return sum(map(treesum, x))
print(treesum(tree))
Output: 50

List comprehension works but not generator expression [duplicate]

This question already has answers here:
A safe max() function for empty lists
(7 answers)
Closed 1 year ago.
I am doing this problem:
Given an array of integers arr, a lucky integer is an integer which has a frequency in the array equal to its value.
Return a lucky integer in the array. If there are multiple lucky integers return the largest of them. If there is no lucky integer return -1.
I am using this code, which works and passed all tests:
class Solution:
def findLucky(self, arr: List[int]) -> int:
values = [n for n in arr if n if arr.count(n) == n]
return max(values) if values else -1
However, when I try to change the list comprehension to a generator expression like so:
class Solution:
def findLucky(self, arr: List[int]) -> int:
values = (n for n in arr if n if arr.count(n) == n)
return max(values) if values else -1
I get this error:
Traceback (most recent call last):
File "...", line 10, in <module>
print(Solution().findLucky([2, 3, 4]))
File "...", line 7, in findLucky
return max(values) if values else -1
ValueError: max() arg is an empty sequence
Why does a list comprehension work whereas a generator expression fails?
I've tried converting the generator expression to a list in the comprehension, but that won't work because then the generator is exhuased.
Every generator is true, so you always call max(values) even if the generator is "empty". The right way to do it is to tell max what to do then:
return max(values, default=-1)

TypeError: 'int' object is not iterable

There is an error when I execute This code-
for i in len(str_list):
TypeError: 'int' object is not iterable
How would I fix it? (Python 3)
def str_avg(str):
str_list=str.split()
str_sum=0
for i in len(str_list):
str_sum += len(str_list[i])
return str_sum/i
You are trying to loop over in integer; len() returns one.
If you must produce a loop over a sequence of integers, use a range() object:
for i in range(len(str_list)):
# ...
By passing in the len(str_list) result to range(), you get a sequence from zero to the length of str_list, minus one (as the end value is not included).
Note that now your i value will be the incorrect value to use to calculate an average, because it is one smaller than the actual list length! You want to divide by len(str_list):
return str_sum / len(str_list)
However, there is no need to do this in Python. You loop over the elements of the list itself. That removes the need to create an index first:
for elem in str_list
str_sum += len(elem)
return str_sum / len(str_list)
All this can be expressed in one line with the sum() function, by the way:
def str_avg(s):
str_list = s.split()
return sum(len(w) for w in str_list) / len(str_list)
I replaced the name str with s; better not mask the built-in type name, that could lead to confusing errors later on.
For loops requires multiple items to iterate through like a list of [1, 2, 3] (contains 3 items/elements).
The len function returns a single item which is an integer of the length of the object you have given it as a parameter.
To have something iterate as many times as the length of an object you can provide the len functions result to a range function. This creates an iterable allowing you to iterate as any times as the length of the object you wanted.
So do something like
for i in range(len(str_list)):
unless you want to go through the list and not the length of the list. You can then just iterate with
for i in str_list:
def str_avg(str):
str_list = str.split()
str_sum = len(''.join(str_list)) # get the total number of characters in str
str_count = len(str_list) # get the total words
return (str_sum / str_count)
While running for loop we need to provide any iterable object. If we use len(str_list) then it will be an integer value. We can not make an integer iterable.
Solution - Using range() function.
for i in range(len(str_list)):
Get complete detail in this article. enter link description here

Python aggregate on a generator

I have a generator that returns a list in each iteration. Each element of the list could be either 0 or 1. I want to count the total number of elements returned (including both 0 and 1) and the total number of 1 returned. I tried to implement this using reduce function like this :
t = reduce( (lambda x,y:(y[0]+1,y[1]+x)), gen_fn(), (0,0))
gen_fn() above is the generator that returns part of the list in each yield statement. I wanted to implement it by initializing with a tuple (0,0) for count. Given that the elements returned from generator are following :
[0, 1, 1, 0, 1]
My expected output for t is (5,3). But my code is failing with this error message :
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
Can anybody help me identify the problem? My lack of experience with reduce and lambda functions is preventing me from figuring out what I am doing wrong. Thanks in advance.
I think the best answer here is to keep it simple:
count = 0
total = 0
for item in gen_fn():
count += 1
total += item
Using reduce() here only makes your code less readable.
If your question is code golf and you want a one liner (while keeping lazy evaluation), then you want:
count, total = collections.deque(zip(itertools.count(1), itertools.accumulate(gen_fn())), maxlen=1).pop()
Of course, you'd be mad to pick such a construction over the simple solution.
Edit:
If the generator yields multiple smaller parts, then simply use itertools.chain.from_iterable(gen_fn()) to flatten it.
You have the lambda arguments the wrong way around; the first argument (x) is the total so far (the tuple) and the second (y) is the new value (the integer). Try:
t = reduce((lambda x, y: (x[0]+1, x[1]+y)), gen_fn(), (0,0))
Using a dummy function:
def gen_fn():
for x in [0, 1, 1, 0, 1]:
yield x
I get (5, 3).
This equivalent implementation of reduce from the docs might make things clearer:
def reduce(function, iterable, initializer=None):
it = iter(iterable)
if initializer is None:
try:
initializer = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = initializer
for x in it:
accum_value = function(accum_value, x) # note value so far is first arg
return accum_value
As jonrsharpe has pointed out, you are using your lambda arguments backwards, given the way reduce works. However, there may be a further issue with how you're adding things up, if each item yielded from your generator is a list.
This issue is that your y value (the item yielded by the generator) is not a single number, but a list. You need to count its length and the number of 1s it has, so you probably want your lambda function to be:
lambda x, y: (x[0]+len(y), x[1]+sum(y))
How about taking a completely different approach?
t = [(len(row), len(filter(lambda x: x == 1, row))) for row in gen_fn()]

python: iteration over non sequence error

Just starting problem 164 for project euler, and i want a function to output a list of the sum of each set of 3 consecutive digits in a 20 digit number. Here is my function.
def func(n):
sumlist = []
m = str(n)
for i in range(0,18):
sumlist.append(sum(int(m[i])+int(m[i+1])+int(m[i+2])))
return sumlist
I keep getting the iteration over non sequence error and i can't figure out why i should. Any help would be appreciated, thanks.
EDIT
The full traceback is:
Traceback (most recent call last):
File "peproblem164.py", line 8, in ? print func(11201201201201201201)
File "peproblem164.py", line 5, in func
sumlist.append(sum(int(m[i])+int(m[i+1])+int(m[i+2])))
TypeError: iteration over non-sequence'
That's because
int(m[i]) + int(m[i+1]) + int(m[i+2])
isn't a sequence. It's an int. I believe you mean:
sumlist.append(sum((int(m[i]), int(m[i+1]), int(m[i+2]))
this will work because
(int(m[i]), int(m[i+1]), int(m[i+2]))
is a sequence.
All in all, it would be easier to work with a list of integers, rather than a string, so it would make sense to convert m into ints first:
m = map(int, str(n))
and then use Python's slicing syntax to get the subsequence sums:
for i in range(18): #Leaving out the start argument is equivalent to range(0, 18)
sumlist.append(sum(m[i:i+3]))
and it would be even more pythonic to use a list comprehension instead (not to mention faster):
sumlist = [m[i:i+3] for i in range(18)] #same thing as the for loop
You really don't need the sum call. Replace
sumlist.append(sum(int(m[i])+int(m[i+1])+int(m[i+2])))
with
sumlist.append(int(m[i])+int(m[i+1])+int(m[i+2]))

Categories