1. Basic Search Capabilities in Collections
A lot of times, the words “search” and “filtering” are used like they're the same thing, but in programming they're actually different.
- Filtering — that's when you want to get all the elements in a collection that match some condition (like, all numbers greater than 10).
- Search — that's when you usually want just one element: the first one that fits, an element with a certain value, or just to check if it's in the collection at all.
If you think of it like a library: filtering is like grabbing all the books on “Space”, and searching is like asking: “Do you have the book ‘Ulysses’?” or “Where's the first book with a blue cover?”.
All the main .NET collections (arrays, lists, sets, dictionaries, etc.) support several ways to search for elements or check if something's there.
Let's check out the most common methods:
| Collection | Check if exists | Find index | Find element | Find by key |
|---|---|---|---|---|
|
|
|
|
- |
(array) |
/ |
|
- | - |
|
|
- | - | - |
|
, |
- | - | Has indexer |
Some methods are only available on certain types.
2. Searching in Lists: List<T>
List<T> is one of the most universal collections for storing elements with random access. Here are the main search methods:
Checking if an element exists: Contains
This method tells you if the element you want is in the list:
List<string> fruits = new List<string> { "Apple", "Banana", "Kiwi" };
bool hasKiwi = fruits.Contains("Kiwi"); // true
bool hasMango = fruits.Contains("Mango"); // false
Console.WriteLine(hasKiwi); // True
Under the hood, Contains just loops through the collection using the Equals method.
Finding the index: IndexOf
If you want to know exactly where the element is (its number in the list):
int index = fruits.IndexOf("Banana"); // 1 (zero-based index)
int absentIndex = fruits.IndexOf("Watermelon"); // -1 (if not found)
Console.WriteLine(index);
Console.WriteLine(absentIndex);
Find the first match: Find
You can search not just by value, but by a condition! For that, use the Find method (or its index buddy — FindIndex):
// Find the first fruit with a name longer than 4 characters
string longFruit = fruits.Find(fruit => fruit.Length > 4); // "Apple"
Console.WriteLine(longFruit);
Heads up: if nothing is found, you get the default value for the type (null for reference types).
Finding multiple elements: FindAll
If you want filtering (all matches), use FindAll:
// All fruits whose name contains the letter 'i'
List<string> withI = fruits.FindAll(f => f.Contains('i'));
foreach (var fruit in withI)
Console.WriteLine(fruit); // "Kiwi"
3. Searching in Arrays: Array class methods
Arrays don't have Find methods inside them (like List<T>), but there's a static Array class for that:
int[] numbers = { 1, 2, 3, 2, 4 };
int pos = Array.IndexOf(numbers, 2); // 1 — first index of 2
int lastPos = Array.LastIndexOf(numbers, 2); // 3 — last index
Console.WriteLine(pos + ", " + lastPos);
If you want to find an element by condition, you can use a simple loop:
int firstGreaterThanTwo = -1;
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] > 2)
{
firstGreaterThanTwo = numbers[i];
break;
}
}
Console.WriteLine(firstGreaterThanTwo); // 3
4. Universal Methods for All Collections
A lot of collections provide search methods (like Contains, IndexOf, Find, etc.). If you need something more complicated, just write a loop to go through the elements.
Example: Check if there's a fruit starting with "B"
bool hasB = false;
foreach (var f in fruits)
{
if (f.StartsWith("B"))
{
hasB = true;
break;
}
}
Console.WriteLine(hasB); // True
Example: Find the first fruit containing "i"
string withI = null;
foreach (var f in fruits)
{
if (f.Contains('i'))
{
withI = f;
break;
}
}
Console.WriteLine(withI); // "Kiwi"
Example: Searching custom objects
class Student
{
public string Name;
public int Group;
public int Id;
}
List<Student> students = new List<Student>
{
new Student { Name = "Ivan", Group = 101, Id = 1 },
new Student { Name = "Maria", Group = 101, Id = 2 },
new Student { Name = "Pyotr", Group = 102, Id = 3 },
};
// Looking for the student with Id == 2
Student maria = null;
foreach (var s in students)
{
if (s.Id == 2)
{
maria = s;
break;
}
}
if (maria != null)
Console.WriteLine(maria.Name); // "Maria"
else
Console.WriteLine("Student not found");
5. Searching in HashSet<T>: just “Is it there or not?”
Sets (HashSet<T>) are made for fast “is it there or not” checks. They don't let you search by index, but they're super fast at checking if something's in there:
HashSet<int> set = new HashSet<int> { 1, 3, 5, 7 };
bool hasThree = set.Contains(3); // True
Console.WriteLine(hasThree);
// If you want to search by condition (like, are there any even numbers?):
bool hasEven = false;
foreach (var x in set)
{
if (x % 2 == 0)
{
hasEven = true;
break;
}
}
Console.WriteLine(hasEven); // False
6. Searching in Dictionaries: Dictionary<TKey, TValue>
A dictionary is a collection of “key-value” pairs. Searching by key is its superpower.
Checking if a key exists
Dictionary<int, string> idToName = new Dictionary<int, string>
{
{ 1, "Vasya" }, { 2, "Katya" }
};
if (idToName.ContainsKey(2))
Console.WriteLine(idToName[2]); // "Katya"
Searching for a value by key: safely!
if (idToName.TryGetValue(3, out string result))
Console.WriteLine(result);
else
Console.WriteLine("No student with that Id"); // This is what gets printed
Searching by value (rare and slow):
bool containsVasya = idToName.ContainsValue("Vasya");
Console.WriteLine(containsVasya); // True
Find an entry by condition on value or key
// First Id where the name starts with "K"
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
if (pair.Value.StartsWith("K"))
{
entry = pair;
found = true;
break;
}
}
if (found)
Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: Katya"
7. Handy Details
Table of Search Methods
| Collection type | Check if exists Contains | Find index IndexOf | Find by condition Find | Safe search by key TryGetValue |
|---|---|---|---|---|
|
Yes | Yes | Yes () |
- |
(array) |
Yes (/loop) |
Yes () |
Loop | - |
|
Yes | - | No (only manually) | - |
|
Yes () |
- | Manually by key/value | Yes |
Iterative Search: Writing It Yourself
For full immersion, let's try writing our own search method for a collection. For example, find the index of the first element that's greater than a given value:
static int FindFirstGreaterIndex(IEnumerable<int> collection, int minValue)
{
int index = 0;
foreach (var item in collection)
{
if (item > minValue)
return index;
index++;
}
return -1; // not found
}
var nums = new List<int> { 1, 4, 7, 2 };
Console.WriteLine(FindFirstGreaterIndex(nums, 3)); // 1 (number 4)
Here we're not tied to the type (List<T>, int[], even HashSet<T>), or to the internal implementation.
Using Search in Real Tasks
- Searching for a user by login or e-mail in a database.
- Checking if a certain product is in the shopping cart.
- Fast search for settings by parameter name.
- Checking if a certain step has already been completed (like in a workflow).
- Finding the error position in a log array.
Most interviews for middle developers always include the question: “How do you search for elements in a collection? How would you write a method that returns the first/all/index of an element by condition?” So practicing search is super useful!
8. Typical Mistakes and Search Gotchas
Important: search methods depend on how objects are compared. For example, if you add your own classes to a list, the default comparison is by reference! For search to work “by content”, you need to override the Equals and GetHashCode methods (more on that in the next lectures).
Example of a problem:
var s1 = new Student { Name = "Egor", Id = 42 };
students.Add(s1);
// Now we create a new object with the same Id and name:
var s2 = new Student { Name = "Egor", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False (!)
The compiler compares not by fields, but by reference (these are different objects).
GO TO FULL VERSION