Artifex
Artifex

Reputation: 27

List index out of range, but why?

def list_2d_locations(rows, columns, low_range, high_range):
    matrix = list_2d_generate(rows, columns, low_range, high_range)
    print(matrix)
    low_val = high_val = matrix[0][0]
    for i in range(rows):
        for j in range(columns):
            if matrix[i][j] < low_val:
                low_val = matrix[i][j]
                low_loc = [i][j]
            if matrix[i][j] > high_val:
                high_val = matrix[i][j]
                high_loc = [i][j]
    return low_val, high_val, low_loc, high_loc

I have here a function which is supposed to find the smallest and largest number within a list of lists (i.e. a matrix) and return the actual value, and the position of that value within the matrix. Now my problem is that both high_loc = [i][j] and low_loc = [i][j] give me the error of "list index out of range" and I don't understand why. Wouldn't the if statements also be out of range then by the same logic?

Upvotes: 0

Views: 114

Answers (2)

SZIEBERTH &#193;d&#225;m
SZIEBERTH &#193;d&#225;m

Reputation: 4184

Here is a solution. It is clean. However, for big matrices it should be optimized to iterate only once over them and collect all info during that iteration.

m = [[3,5,1], [56,43,12], [4,52,673]]

def f(matrix):
    cols = len(matrix[0])
    flatten = [val for row in matrix for val in row]
    min_val, max_val = min(flatten), max(flatten)
    min_i, max_i = flatten.index(min_val), flatten.index(max_val)
    return min_val, max_val, divmod(min_i, cols), divmod(max_i, cols)

>>> f(m)
(1, 673, (0, 2), (2, 2))

EDIT: You know what, here is the optimized version:

def f(matrix):
    try:
        min_val, max_val = matrix[0][0], matrix[0][0]
    except IndexError:
        raise ValueError("Expected a real matrix.") from None
    genexp = (val for row in matrix for val in row)
    cols = len(matrix[0])
    min_i, max_i = (0, 0), (0, 0)
    for i, val in enumerate(genexp):
        if val < min_val:
            min_val, min_i = val, divmod(i, cols)
        elif val > max_val:
            max_val, max_i = val, divmod(i, cols)
    return min_val, max_val, min_i, max_i

EDIT2:

Here are an experience for better understanding which could be done either by you. I suggest you do the same if you want to understand a code.

>>> matrix = [[3,5,1], [56,43,12], [4,52,673]]
>>> flatten = [val for row in matrix for val in row]
>>> flatten
[3, 5, 1, 56, 43, 12, 4, 52, 673]
>>> flatten.index(56)
3
>>> divmod(3,3)
(1, 0)
>>> for elem in enumerate(["one", "two", "three"]):
...     elem
...
(0, 'one')
(1, 'two')
(2, 'three')

Upvotes: 1

Mark Dickinson
Mark Dickinson

Reputation: 30561

The problem is in the line low_loc = [i][j]. In the expression on the right-hand side of the = sign, [i] is a list with a single element, and [i][j] represents an attempt to extract the element at position j from that list. That will fail unless j == 0. Perhaps you wanted low_loc = [i, j] or low_loc = (i, j) instead? (The same comments apply to the line high_loc = [i][j], of course.)

Upvotes: 1

Related Questions