Finding lowest value within a nested list?

此生再无相见时 提交于 2019-12-01 12:26:14
Marcin

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 

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.

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]]]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!