C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
192: Zip and Chunk
I was working on a telemetry project the other day where I had two separate arrays coming off a hardware sensor: one containing timestamps and another containing the actual temperature readings. They were perfectly aligned by index, but they were separate collections. My first instinct was to just use a for loop with an index i to pair them up, but that always feels a bit "C-style" and clunky when we have LINQ at our disposal.
Pairing lists without the index noise
Let's look at what I started with. I've got my timestamps and my values:
var timestamps = new[] { "10:00", "10:01", "10:02", "10:03", "10:04" };
var readings = new[] { 22.5, 22.7, 22.1, 23.0, 22.8 };
If I want to combine these into a single object or a tuple, I could do this:
var combined = new List<string>();
for (int i = 0; i < timestamps.Length; i++)
{
combined.Add($"{timestamps[i]} -> {readings[i]}");
}
It works, sure. But it's brittle. If readings happens to be shorter than timestamps, this throws an IndexOutOfRangeException. I have to add manual checks. This is exactly why Zip exists. I tried swapping the loop for Zip, and it cleaned up the logic immediately:
var paired = timestamps.Zip(readings, (time, temp) => new { time, temp });
foreach (var item in paired)
{
Console.WriteLine($"At {item.time}, the temp was {item.temp}");
}
The beauty here is that Zip stops as soon as the shortest sequence ends. No more manual bounds checking. I just tell it: "Take this sequence and that sequence, and here is the function to merge them."
Dealing with API batch limits
Now, here is where it got annoying. I needed to send these paired readings to a remote logging API, but the API has a strict limit: it only accepts a maximum of 2 readings per request. If I send the whole list, I get a 400 Bad Request.
My initial thought was to use Skip() and Take() inside a while loop. I started sketching it out:
int batchSize = 2;
for (int i = 0; i < paired.Count(); i += batchSize)
{
var batch = paired.Skip(i).Take(batchSize);
// Send batch to API...
}
Wait, that's actually pretty inefficient. Skip(i) has to iterate through the first i elements every single time. For a small list, it's fine, but if I'm processing thousands of sensor readings, I'm wasting a lot of cycles. I remember seeing a newer method in .NET called Chunk. Let's see if it does what I think it does.
I replaced the loop with this:
var batches = paired.Chunk(2);
foreach (var batch in batches)
{
Console.WriteLine("--- New API Request ---");
foreach (var reading in batch)
{
Console.WriteLine($"{reading.time}: {reading.temp}");
}
}
That's much cleaner. Chunk doesn't just skip and take; it slices the sequence into arrays of the size you specify. If the total number of elements isn't perfectly divisible by the chunk size, the last chunk is just smaller—it doesn't crash or pad it with nulls. It just gives you what's left.
The final pipeline
When you put them together, you get this really elegant flow: you zip your disparate data sources into a unified model, then chunk that model into the exact sizes your external dependencies require. It turns a messy set of loops and index counters into a declarative pipeline.
var telemetryData = timestamps
.Zip(readings, (time, temp) => new { time, temp })
.Chunk(2);
// Now we have an IEnumerable of arrays, ready for the API.📋 Practical Task
Building a Paginated User-Role Mapper
You have two lists: one containing usernames and one containing assignedRoles. The lists are sorted such that the username at index 0 belongs to the role at index 0, and so on.
Your task is to create a program that:
- Uses
Zipto pair the usernames and roles into a tuple or an anonymous object. - Uses
Chunkto divide these pairs into "pages" of 3 users per page. - Prints each page to the console, clearly labeling the page number (e.g., "Page 1", "Page 2") and listing the users and their roles within that page.
Starter Data:
string[] usernames = { "Alice", "Bob", "Charlie", "Dan", "Eve", "Frank", "Grace", "Heidi" };
string[] roles = { "Admin", "User", "User", "Editor", "Admin", "User", "Editor", "User" };There are no comments for now.