Find a 2x2 vowel square in a grid (Coderbyte 'Vowel Square' problem) My solution to the Coderbyte task 'Vowel Square'. It's a simple task, but I'm sure there's a more Pythonic solution than mine.

The problem:

Vowel Square
Have the function VowelSquare(strArr) take the strArr parameter being passed
which will be a 2D matrix of some arbitrary size filled with letters from
the alphabet, and determine if a 2x2 square composed entirely of vowels
exists in the matrix. For example: strArr is ["abcd", "eikr", "oufj"] then
this matrix looks like the following:

a b c d

e i k r

o u f j


Within this matrix there is a 2x2 square of vowels starting in the second
row and first column, namely, ei, ou. If a 2x2 square of vowels is found
your program should return the top-left position (row-column) of the square,
so for this example your program should return 1-0. If no 2x2 square of
vowels exists, then return the string not found. If there are multiple
squares of vowels, return the one that is at the most top-left position
in the whole matrix. The input matrix will at least be of size 2x2.

Examples:


Input: ["aqrst", "ukaei", "ffooo"]
Output: 1-2,
Input: ["gg", "ff"]
Output: not found


My solution:

VOWELS = "aeiouAEIOU"

def find_vowel_square(strs: list):
    """Return the top left grid ref of any 2x2 sq composed of vowels only

    If more than one 2x2 sq exists. Return that which is at the most top-left
    position.
    """
    height, width = len(strs), len(strs[0])

    # Ensure all strings within strs are of equal length
    assert all(len(s) == width for s in strs)

    for string_i in range(height-1):
        for i in range(width-1):
            if strs[string_i][i] in VOWELS and strs[string_i][i+1] in VOWELS:
                if strs[string_i+1][i] in VOWELS and strs[string_i+1][i+1] in VOWELS:
                    return f"{i}-{string_i}"

    return "Not found"

assert find_vowel_square(strs=["aqree", "ukaei", "ffooo"]) == "3-0"
assert find_vowel_square(strs=["aqrst", "ukaei", "ffooo"]) == "2-1"
assert find_vowel_square(strs=["gg", "ff"]) == "Not found"
These are all relatively minor notes:


You can use typing.List to provide a better type definition for the parameter (List[str]), precisely defining what it's a list of.
I always try to avoid giving variables names that are just a variation on the Python type to tell me what its type is (that's the type annotation's job); the problem description calls this the "matrix" so I'll use that.
If the problem description doesn't say what to do with invalid input, I'd assume it's fine (and preferable) to just let the code raise an exception if any assumptions are violated.
If you're checking that a bunch of iterable conditions are all true, I think it generally looks nicer to use the all function than to do a bunch of ands.
For iterating over indices (i.e. where it's super obvious from context what the variable represents and a longer name only serves to distract) I always prefer using short, generic variable names like i, j or x, y.


from typing import List

VOWELS = "aeiouAEIOU"

def find_vowel_square(matrix: List[str]):
    """Return the top left grid ref of any 2x2 sq composed of vowels only

    If more than one 2x2 sq exists. Return that which is at the most top-left
    position.
    """
    for y in range(len(matrix) - 1):
        for x in range(len(matrix[y]) - 1):
            if all (matrix[i][j] in VOWELS 
                    for i, j in [(y, x), (y+1, x), (y, x+1), (y+1, x+1)]):
                return f"{x}-{y}"

    return "Not found"