Halloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowHalloween DealsAmazon USOctober picks are moving - compare before checkoutAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk7 min

VB.NET Generic Search Function for a Two-Dimensional Array

A nested-loop generic function is the reliable way to search a rectangular VB.NET T(,) array. Learn how to return values and coordinates, handle duplicates and Nothing, support custom predicates, and choose alternatives for repeated lookups.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a rectangular VB.NET array such as T(,), the dependable generic search pattern is a pair of nested loops. A function that accepts Predicate(Of T) can search integers, strings, dates, structures, classes, or custom conditions while returning the first match and its row and column.

Array.Find(Of T) is not a direct substitute: its documented generic overload accepts a one-dimensional T() array and returns a matching value, not two-dimensional coordinates. See Microsoft’s Array.Find documentation.

As an Amazon Associate I earn from qualifying purchases.

The production-ready generic function

The result should include an explicit Found flag. Returning only T is ambiguous because 0, False, Nothing, or a default structure may be a legitimate array value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Imports System

Public NotInheritable Class SearchHit(Of T)
    Public ReadOnly Property Found As Boolean
    Public ReadOnly Property Row As Integer
    Public ReadOnly Property Column As Integer
    Public ReadOnly Property Value As T

    Private Sub New(found As Boolean,
                    row As Integer,
                    column As Integer,
                    value As T)
        Me.Found = found
        Me.Row = row
        Me.Column = column
        Me.Value = value
    End Sub

    Public Shared Function Match(row As Integer,
                                 column As Integer,
                                 value As T) As SearchHit(Of T)
        Return New SearchHit(Of T)(True, row, column, value)
    End Function

    Public Shared Function NotFound() As SearchHit(Of T)
        Return New SearchHit(Of T)(False, -1, -1, Nothing)
    End Function
End Class

Public Module ArraySearch
    Public Function Find2D(Of T)(
        array As T(,),
        match As Predicate(Of T)
    ) As SearchHit(Of T)

        If array Is Nothing Then
            Throw New ArgumentNullException(NameOf(array))
        End If

        If match Is Nothing Then
            Throw New ArgumentNullException(NameOf(match))
        End If

        Dim firstRow As Integer = array.GetLowerBound(0)
        Dim lastRow As Integer = array.GetUpperBound(0)
        Dim firstColumn As Integer = array.GetLowerBound(1)
        Dim lastColumn As Integer = array.GetUpperBound(1)

        For row As Integer = firstRow To lastRow
            For column As Integer = firstColumn To lastColumn
                Dim value As T = array(row, column)

                If match(value) Then
                    Return SearchHit(Of T).Match(row, column, value)
                End If
            Next
        Next

        Return SearchHit(Of T).NotFound()
    End Function
End Module

The predicate determines what counts as a match. The function scans the first dimension, then the second, and stops at the first matching cell.

Using the function

Exact integer search

Dim numbers(,) As Integer = {
    {10, 20, 30},
    {40, 50, 60},
    {70, 80, 90}
}

Dim result = Find2D(numbers, Function(value) value = 50)

If result.Found Then
    Console.WriteLine($"Found {result.Value} at row {result.Row}, column {result.Column}")
Else
    Console.WriteLine("Value was not found.")
End If

This prints:

Found 50 at row 1, column 1

Case-insensitive string search

Dim names(,) As String = {
    {"Alice", "Bob"},
    {"Carol", "Diana"}
}

Dim result = Find2D(
    names,
    Function(value) String.Equals(
        value,
        "diana",
        StringComparison.OrdinalIgnoreCase))

If result.Found Then
    Console.WriteLine($"Found at ({result.Row}, {result.Column})")
End If

The predicate also supports ranges, compound conditions, and property checks:

Dim result = Find2D(numbers, Function(value) value >= 40 AndAlso value <= 60)

Why Array.Find does not solve this directly

This valid call uses a one-dimensional array:

Dim values() As Integer = {10, 20, 30}

Dim value = Array.Find(
    values,
    Function(number) number > 15)

The documented signature is based on T(), not rectangular T(,). It returns the first matching element and does not preserve row and column information. Array.FindIndex has the same one-dimensional focus, returning a linear index rather than a two-dimensional coordinate. A nested-loop function is therefore the clearest general solution for a rectangular array.

This does not mean no API can ever work with a base Array; APIs such as Array.GetValue can retrieve values through multidimensional indexes. However, using Array as the main parameter loses compile-time guarantees about the element type, rank, and rectangular shape. Prefer T(,) for this specific function.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Array declaration, dimensions, and traversal order

A rectangular two-dimensional array uses a comma in its type declaration:

Dim values(,) As Integer
Dim values(2, 3) As Integer

For a normal zero-based declaration, values(2, 3) has three elements in the first dimension and four in the second. Access uses comma-separated indexes:

Dim item As Integer = values(row, column)
values(row, column) = 123

The recommended implementation searches in the order imposed by its loops:

(0,0), (0,1), (0,2), ...
(1,0), (1,1), (1,2), ...

For duplicate values, “first” means the first coordinate encountered in that order. This is a property of the function’s traversal, not a separate multidimensional search order supplied by Array.Find.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Most ordinary VB.NET arrays are zero-based, so this loop is also common:

For row As Integer = 0 To array.GetLength(0) - 1

For reusable code, GetLowerBound and GetUpperBound are safer. GetLength(dimension) returns the number of elements in a dimension; the bound methods return its actual lowest and highest indexes. This also supports arrays created with nonzero lower bounds through .NET array APIs. Microsoft’s Visual Basic array documentation covers these dimension methods and indexing rules.

A convenient equality overload

Callers performing exact equality searches should not need to write a predicate every time:

Imports System.Collections.Generic

Public Function Find2D(Of T)(
    array As T(,),
    value As T,
    Optional comparer As IEqualityComparer(Of T) = Nothing
) As SearchHit(Of T)

    If array Is Nothing Then
        Throw New ArgumentNullException(NameOf(array))
    End If

    If comparer Is Nothing Then
        comparer = EqualityComparer(Of T).Default
    End If

    Return Find2D(
        array,
        Function(current) comparer.Equals(current, value))
End Function

Usage is concise:

Dim result = Find2D(numbers, 50)

Use the predicate overload when you need case-insensitive text, ranges, projections, culture-specific rules, or compound criteria. The equality overload uses EqualityComparer(Of T).Default; it does not automatically mean case-insensitive string comparison.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Returning every matching coordinate

If duplicate values matter, collect all hits instead of returning immediately:

Imports System.Collections.Generic

Public Function FindAll2D(Of T)(
    array As T(,),
    match As Predicate(Of T)
) As List(Of SearchHit(Of T))

    If array Is Nothing Then
        Throw New ArgumentNullException(NameOf(array))
    End If

    If match Is Nothing Then
        Throw New ArgumentNullException(NameOf(match))
    End If

    Dim results As New List(Of SearchHit(Of T))()

    For row As Integer = array.GetLowerBound(0) To array.GetUpperBound(0)
        For column As Integer = array.GetLowerBound(1) To array.GetUpperBound(1)
            Dim value As T = array(row, column)

            If match(value) Then
                results.Add(SearchHit(Of T).Match(row, column, value))
            End If
        Next
    Next

    Return results
End Function
Dim matches = FindAll2D(
    numbers,
    Function(value) value Mod 20 = 0)

The scan still takes O(rows × columns) time, while the returned collection uses O(k) additional space for k matches.

When only a Boolean is needed

A Boolean-only search avoids creating a result object:

Public Function Contains2D(Of T)(
    array As T(,),
    match As Predicate(Of T)
) As Boolean

    If array Is Nothing Then
        Throw New ArgumentNullException(NameOf(array))
    End If

    If match Is Nothing Then
        Throw New ArgumentNullException(NameOf(match))
    End If

    For row As Integer = array.GetLowerBound(0) To array.GetUpperBound(0)
        For column As Integer = array.GetLowerBound(1) To array.GetUpperBound(1)
            If match(array(row, column)) Then
                Return True
            End If
        Next
    Next

    Return False
End Function

An empty array naturally returns False. A null array or null predicate is rejected with ArgumentNullException, matching the conventional behavior documented for Array.Find.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rectangular arrays are not jagged arrays

These declarations describe different types:

Dim rectangular(,) As Integer
Dim jagged()() As Integer

A rectangular array has one fixed two-dimensional shape. A jagged array is an array whose elements are arrays, so rows may have different lengths. The T(,) function cannot accept T()().

For a jagged array, use row-specific lengths and indexing such as array(row)(column):

Public Function FindJagged(Of T)(
    array As T()(),
    match As Predicate(Of T)
) As SearchHit(Of T)

    If array Is Nothing Then
        Throw New ArgumentNullException(NameOf(array))
    End If

    If match Is Nothing Then
        Throw New ArgumentNullException(NameOf(match))
    End If

    For row As Integer = 0 To array.Length - 1
        If array(row) Is Nothing Then Continue For

        For column As Integer = 0 To array(row).Length - 1
            Dim value As T = array(row)(column)

            If match(value) Then
                Return SearchHit(Of T).Match(row, column, value)
            End If
        Next
    Next

    Return SearchHit(Of T).NotFound()
End Function
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance and alternatives

For R rows and C columns, a first-match search has:

  • Worst-case time: O(R × C).
  • Best-case time: O(1) when the first cell matches.
  • Extra space: O(1).
  • All-match space: O(k), where k is the number of matches.

This is a linear scan over the array’s total cells. A generic parameter provides type safety and reuse; it does not automatically make scanning faster.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If only a Boolean is needed, a LINQ query can flatten the array:

Dim found As Boolean =
    numbers.Cast(Of Integer)().
        Any(Function(value) value = 50)

This is reasonable when coordinates are irrelevant. Once row and column matter, nested loops are more transparent and avoid having to reconstruct coordinates from a flattened sequence.

Array.BinarySearch is not a general replacement. Binary search requires a one-dimensional array sorted according to compatible comparison rules; a rectangular matrix is not automatically arranged for that algorithm. See Microsoft’s Array.BinarySearch documentation.

For frequent exact lookups, build an index such as Dictionary(Of T, List(Of Coordinate)) rather than scanning the entire array for every request. If the data represents records, a List(Of T) where T is a class or structure may be a better model. For persistent or query-heavy data, a database index is usually more appropriate.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Important edge cases

  • Missing value: check result.Found; do not infer failure from Value.
  • Nothing values: a reference-type array can legitimately contain Nothing, so search with Function(value) value Is Nothing.
  • Duplicates: Find2D returns the first hit; use FindAll2D for every hit.
  • Empty arrays: return not found without throwing.
  • Nonzero bounds: use the actual lower and upper bounds rather than hard-coding zero.
  • Custom objects: predicates can inspect properties, for example Function(person) person.Id = 42.

Tests worth writing

A reusable search helper should be checked against:

  • a match in the first cell;
  • a match in the last cell;
  • no match;
  • duplicate values;
  • an empty array;
  • case-sensitive and case-insensitive string rules;
  • a legitimate Nothing value;
  • custom predicates and custom equality comparers;
  • nonzero lower-bound arrays if that scenario is supported.

Decision guide

Need Use
One lookup in a rectangular array Find2D(Of T) with nested loops
Exact equality with a simple call The equality overload
Only existence Contains2D(Of T)
Every duplicate match FindAll2D(Of T)
Rows with different lengths A separate jagged-array function
Many repeated exact lookups A dictionary index
Query-heavy structured data Records, collections, or a database

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.