Im trying to write a function that takes a list and can print the lowest integer that is within that list. Now i'm trying to figure out what to do where this works with nested lists that if the lowest number is within one of those nested lists then overall it will print that number. My code is here:
def listMin():
list2 = [3,4,[2,99,8],7]
for i in range (len(list2)):
if type(list2[i]) == type([]):
y=min(i)
list2.append(y)
print "hello"
if len(list2)== 0:
return None
else:
x= min(list2)
print x
listMin()
while this seems like it should print the number 2 it doesnt and just gives me an error once it reaches the nested list saying:
TypeError: 'int' object is not iterable
ive tried multiple things but i'm having a hard time as to why this sort of thing isn't working.
Nesting One Deep
In your example, the list is nested only one deep. If this is the case in general, then try:
>>> list2 = [3,4,[2,99,8],7]
>>> min(x if isinstance(x, int) else min(x) for x in list2)
2
Nesting of Arbitrary Depth
If deeper nesting is allowed, define this recursive function:
>>> def rmin(lst): return min(x if isinstance(x, int) else rmin(x) for x in lst)
...
In operation:
>>> rmin(list2)
2
Or, with deeper nesting:
>>> list3 = [3,4,[[2,99],8],7]
>>> rmin(list3)
2
>>> list4 = [3, 4, [[2, [99, 1]], 8], 7]
>>> rmin(list4)
1
How it works
The function rmin consists of the single line:
return min(x if isinstance(x, int) else rmin(x) for x in lst)
As you can see, this is a list comprehension that looks at every value x of the list lst.
Let's divide the argument of min into two parts. The first is:
x if isinstance(x, int) else rmin(x)
This returns x if x is an integer. Otherwise, it calls rmin on x. In the latter case, rmin recursively looks at every value in x and returns the minimum.
The second part of the argument of min is:
for x in lst
This is just the usual for a list comprehension. It extracts each value in lst in turn and assigns it to x.
In general, you could flatten your list of lists and search for min in the flattened list. There are many recipes for flattening. Here is one that I took from here.
import collections
def flatten(iterable):
for el in iterable:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
yield from flatten(el)
else:
yield el
list2 = [3,4,[2,99,8],7]
print(list(flatten(list2)))
# [3, 4, 2, 99, 8, 7]
print(min(flatten(list2)))
# 2
This will work on multiple nested list as well, e.g.:
list2 = [3,4,[2,99,8,[-1,-2]],7]
print(list(flatten(list2)))
# [3, 4, 2, 99, 8, -1, -2, 7]
print(min(flatten(list2)))
# -2
The problem is caused by the line
y=min(i)
where i is an integer but not a list. You probably want y = min(list2[i]).
Note that while you have appended this y back to the original list, the loop would not reach the newly added element, since i will only range up to the original length of the list.
With some simple Python idioms, your original idea can be expressed in a more readable way as follows:
def listMin():
lst = [3,4,[2,99,8],7]
for x in lst:
if type(x) == list:
lst.append(min(x))
print min(lst)
listMin()
When I needed to do something similar, I wrote following:
import copy
def nestedMin(list_):
target_list = copy.deepcopy(list_) # to keep original list unchanged
for index in range (len(target_list)):
if type (target_list[index]) is list:
target_list[index] = nestedMin(target_list[index])
return min(target_list)
I know that it is not very efficient, keeps doing deepcopy; but it's readable and it does the job :)
Example:
list1 = [2,3,[4, -5, [7, -20]]]
print nestedMin(list1) # prints -20
print list1 # prints [2, 3, [4, -5, [7, -20]]]
Related
I have something like this
lists = [ [1,2,3] , [4,5,6] , [8,9,10] ]
and I would like to be able to search for a element of a list and return the entirety of the list
search = 5
result = [4,5,6]
(doesn't have to be that exact format but I would like to be able to see all the elements from the list)
I've looked at other forums and when they search I only returns a true or false value, so I'm not sure if what I would like to do is even possible
You can abuse the next function to get the first element of a iterable that matches a predicate like this:
next(i for i in iterable if p(i))
If no element exists this will throw a StopIteration exception that you can optionally catch.
You can also use the in operator to find if a element is in a list:
5 in [1,2,3]
# False
5 in [4,5,6]
# True
Combining the two:
next(l for l in lists if 5 in l)
You can use numpy
a = np.array(lists)
out = a[np.where(a==5, True, False).any(1)].tolist()
In this kind of situation, I would create a function that will iterate the list and use the in operator to see if the value is in the list or not and finally return the index number of that value x or return False.
def result(srch, list):
for x in list:
if srch in x:
return list[list.index(x)]
return False
lists = [[1, 2, 3], [4, 5, 6], [8, 9, 10]]
search = 15
print(result(search, lists))
Result:
[4, 5, 6]
I'm solving a problem where I must find an unique integer in a list, which is easily solvable using list.count(x). However, I'm having trouble condensing the code into one line.
def find_uniq(arr):
return [x for x in arr if arr.count(x) == 1]
My code works fine, but it returns for example: [2] instead of 2, or [0.55] instead of 0.55.
Is there a way to return the integer instead of the list containing the integer, using list comprehension?
You have already the answers for list-comprehension -- which is a waste of resources.
Your approach, though, is valid since it makes use of everyday/beginner structures. In the same argument, I would like to suggest two things:
avoid the redundant calls to count();
avoid the creation of a list (since all you want is one element a time).
Suppose we have the following array arr:
> arr = [random.randint(0,9) for _ in range(10)]
> arr
[6, 7, 0, 9, 3, 3, 3, 9, 8, 8]
To the first point, you can create a set to reduce the number of counts:
> numbers_set = set(arr)
> numbers_set
{0, 3, 6, 7, 8, 9}
Then you can have a generator with the help of our friend filter:
> unique_numbers = filter(lambda x:arr.count(x)==1, numbers_set)
> print(next(unique_numbers))
0
> print(next(unique_numbers))
6
> print(next(unique_numbers))
7
> print(next(unique_numbers))
StopIteration:
Instead of a list comprehension which produces a list, create a generator from which you take the first element:
return next(x for x in arr if arr.count(x) == 1)
This raises a StopIteration is no element in the list fulfils the criteria; you can instead return a default value like None like so:
return next((x for x in arr if arr.count(x) == 1), None)
It's also questionable whether it's wise to iterate the array again and again with count; depending on its size, that can be very inefficient. It may be more efficient to build a counter first:
from collections import Counter
return next(v for v, c in Counter(arr).items() if c == 1)
If you are sure that You want to return only one integer from the passed list You can change the return to
def find_uniq(arr):
return [x for x in arr if arr.count(x) == 1][0]
But it will return only first element that is unique in the list. If You want to return more of them the list is better approach
Return using indexing.
def find_uniq(arr):
return [x for x in arr if arr.count(x) == 1][0]
I have an exercise for a class in python and i have stuck in question 4:
Create list1 with 200 random integers in space [1,50] and print how many times the number is 10.
Create list2 with 50 integers [1,2,3, ..., 50] and then with append() function join the lists together and display the size.
Delete the data with index number from 99 to 130 and print the list size after deleting items.
Find the maximum and minimum in the list.
My first thought on how to find MAX was to sort the list and get the last value but because 1 item in the list is a list I get this error TypeError: '<' not supported between instances of 'list' and 'int'.
Any ideas on what i have to do?
My code until this question is the one below:
import numpy as np
#Question1
j= 0;
list1 = list(range(200)) #decalre list1
for i in range(200):
list1[i] = np.random.randint(50) #create a list of length 200 with values 0 to 50.
if list1[i] == 10:
j = j+1
print("Number 10 exists ",j,"times in the list.")
#QUESTION2
list2 = list(range(1,51)) #Declare list2
list1.append(list2) #append list2 on list1
print(len(list1))
#QUESTION3
for i in range(99,130): # deletes the items with an index number from 99 to 130
list1.pop(i)
print(len(list1))
You need to flatten your list first before you can make any comparisons as you can't compare a list with an int. In order to do this you can create a recursive function and pass your list
def flatten(l):
return sum(([x] if not isinstance(x, list) else flatten(x) for x in l), [])
Be aware the the list in the above code is a protected name by Python and refers to a list data type. What this function does is compare each item in the lists and checks if it is a list type. If not it adds it to the list, if it is it calls the function again and goes to the next item. You can now run the following:
>>> list1 = [1, 2, 3, 4, [5, 6, 7, 8]]
>>> flatten(list1)
>>> [1, 2, 3, 4, 5, 6, 7, 8]
You can now run Python's built in max() and min() functions
>>> max(flatten(list1))
>>> 8
>>> min(flatten(list1))
>>> 1
Python provides a built-in function to solve this problem:
max([2, 5, 4, 9, 3, 7])
9
I would merge the lists, so appending the items of list2 to list1, so you just have only one axis, then you can use the built-in function
If you don't want to merge the list you can first get the maximum of the list inside the list. Then you can take the maximum of this maximum and the other part of your outer list.
Also you can change this line:
list1[i] = np.random.randint(50)
You can import random (it's a built-in module) instead and do it this way:
list1[i] = random.randint(1, 50)
but probably numpy is faster.
If you want to use numpy you nevertheless need to change the line to:
list1[i] = np.random.randint(1, 50)
You need to change your deleting loop in question 3, because if you delete item 99 then the item 100 is the new item 99, so you delete only each second element. You can fix that problem by going backwards from 130 to 99:
for i in range(130, 99):
I hope i could help you with this question.
Here is the code added in your code:
list2 = list1.pop(-1)
list3 = list1 + list2
print(max(list3))
print(min(list3))
Notice there are two lists in list1!
I am trying to write a simple python code which would find a list k nearest elements to origin using a max heap. My main question is around the usage of the map in python, I tried to code it in this way, and I am getting a list of Nones as output whereas I was expecting max_heap to be populated with values pushed by heappush/heappushpop. Can someone point what's wrong here? Also, I am wondering if I really need to declare max_heap empty list? Is there a way in which I can write this entire logic in 1 statement? Thanks!
def find_closest_k_nums(nums, k):
max_heap = []
for num in nums:
if len(max_heap) == k + 1:
heapq.heappushpop(max_heap, -num)
else:
heapq.heappush(max_heap, -num), nums
return [-s for s in heapq.nlargest(k, max_heap)]
# >>> find_closest_k_nums([1, 5, 6, 3, 8, 9, 10], 4) => [1, 3, 5, 6]
# I tried to write the above function using map but I got an empty list.
def find_closest_k_nums_with_map(nums, k):
max_heap = []
map(lambda x: heapq.heappushpop(max_heap, -x)
if len(max_heap) == k + 1 else heapq.heappush(max_heap, -x), nums)
return [-s for s in heapq.nlargest(k, max_heap)]
# >>> find_closest_k_nums_with_map([1, 5, 6, 3, 8, 9, 10], 4) => []
map returns an iterable which calls the function on demand as you ask for elements from the iterable. More simply:
>>> def increment(x):
... print(f"Calling increment on {x}")
... return x + 1
...
>>> x = [1,2,3]
>>> y = map(increment, x)
Until you iterate over y, increment is never called. Only as you call next on y does increment get called.
>>> next(y)
Calling increment on 1
2
>>> next(y)
Calling increment on 2
3
In order for the elements of nums to be added to your heap in the your second function, you need to (somehow) iterate over the elements that map will yield. For example, pass the map object to list to force iteration:
def find_closest_k_nums_with_map(nums, k):
max_heap = []
list(map(lambda x: heapq.heappushpop(max_heap, -x)
if len(max_heap) == k + 1 else heapq.heappush(max_heap, -x), nums))
return [-s for s in heapq.nlargest(k, max_heap)]
But this is terrible style. You aren't actually interested in the return value of the function getting mapped over nums, only its side effect of updating max_heap. When that is the case, just use a for loop as in your first function.
I have a number of lists that I'm going to use in my program, but I need to be sure that they are all the same length, or I'm going to get problems later on in my code.
What's the best way to do this in Python?
For example, if I have three lists:
a = [1, 2, 3]
b = ['a', 'b']
c = [5, 6, 7]
I could do something like:
l = [len(a), len(b), len(c)]
if max(l) == min(l):
# They're the same
Is there a better or more Pythonic way to do this?
Assuming you have a non-empty list of lists, e.g.
my_list = [[1, 2, 3], ['a', 'b'], [5, 6, 7]]
you could use
n = len(my_list[0])
if all(len(x) == n for x in my_list):
# whatever
This will short-circuit, so it will stop checking when the first list with a wrong length is encountered.
len(set(len(x) for x in l)) <= 1
Latter I ended up writing:
def some(x):
"""Replacement for len(set(x)) > 1"""
if isinstance(x, (set, frozenset)):
return len(x) > 1
s = set()
for e in x:
s.add(e)
if len(s) > 1:
return True
return False
def lone(x):
"""Replacement for len(set(x)) <= 1"""
return not some(x)
Which allows the above to be written as:
lone(len(x) for x in l)
This will stop taking the lengths of the lists as soon as it finds a list with a different length.
A bit of functional Python:
>>> len(set(map(len, (a, b, c)))) == 1
False
Each call to max and min will traverse the whole list, but you don't really need to do that; you can check for the desired property with one traversal:
def allsamelength(lst_of_lsts):
if len(lst_of_lsts) in (0,1): return True
lfst = len(lst_of_lsts[0])
return all(len(lst) == lfst for lst in lst_of_lsts[1:])
This will also short-circuit if one of the lists has a different length from the first.
If l is list of lengths:
l = [len(a), len(b), len(c)]
if len(set(l))==1:
print 'Yay. List lengths are same.'
Otherwise, using the original lists, one could create a list of lists:
d=[a,b,c]
if len(set(len(x) for x in d)) ==1:
print 'Yay. List lengths are same.'