LINQ to Slices
LINQ to Slices
Introduction
In this lesson, you'll learn about linq to slices in Go. Coming from C#, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In C#, you're familiar with linq to slices.
Go has its own approach to linq to slices, which we'll explore step by step.
The Go Way
Let's see how Go handles this concept. Here's a typical example:
package main
numbers := []int{1, 2, 3, 4, 5, 6}
// Go — explicit loops (no built-in LINQ)
var evenDoubled []int
for _, n := range numbers {
if n%2 == 0 {
evenDoubled = append(evenDoubled, n*2)
}
}
// Manual sum/max
sum := 0
max := numbers[0]
for _, n := range numbers {
sum += n
if n > max { max = n }
}Comparing to C#
Here's how you might have written similar code in C#:
var numbers = new[] { 1, 2, 3, 4, 5, 6 };
var evenDoubled = numbers
.Where(n => n % 2 == 0)
.Select(n => n * 2)
.ToList();
int sum = numbers.Sum();
int max = numbers.Max();
bool any = numbers.Any(n => n > 4);You may be used to different syntax or behavior.
Go has no LINQ equivalent — use explicit for loops
You may be used to different syntax or behavior.
This is intentional: Go values readability and explicitness over cleverness
You may be used to different syntax or behavior.
Go 1.23 'slices' and 'maps' packages add some helpers (slices.Contains, slices.Max)
You may be used to different syntax or behavior.
range iterates over slices, maps, strings, and channels — like C# foreach
You may be used to different syntax or behavior.
append() grows slices — there is no Add() method
Step-by-Step Breakdown
1. Slices — Go's Dynamic Array
Go slices are the equivalent of C# List<T>. They are dynamically sized views over arrays. Use append to add elements.
var list = new List<int> { 1, 2, 3 };
list.Add(4);
int first = list[0];
int n = list.Count;nums := []int{1, 2, 3}
nums = append(nums, 4) // must reassign!
first := nums[0]
n := len(nums)
// Slicing (sub-slice)
sub := nums[1:3] // [2, 3] — shares underlying array2. range — The foreach of Go
range iterates over slices, maps, channels, and strings. It yields index and value for slices — use _ to discard the index.
foreach (var item in list) {
Console.WriteLine(item);
}
foreach (var (key, val) in dict) {
Console.WriteLine($"{key}: {val}");
}for i, v := range nums {
fmt.Println(i, v)
}
// Discard index (most common)
for _, v := range nums {
fmt.Println(v)
}
// Map iteration
for key, val := range m {
fmt.Println(key, val)
}3. Filter and Map with Loops
Go has no built-in filter/map. Write explicit loops. The slices package (Go 1.21) adds slices.Contains, slices.Index, slices.Max, etc.
var filtered = list.Where(x => x > 2).ToList();
var mapped = list.Select(x => x * x).ToList();// Filter
var filtered []int
for _, x := range list {
if x > 2 {
filtered = append(filtered, x)
}
}
// Map
mapped := make([]int, len(list))
for i, x := range list {
mapped[i] = x * x
}
// Go 1.21+ slices package helpers
import "slices"
slices.Contains(list, 3)
slices.Max(list)4. Maps — Go's Dictionary
Go maps are the equivalent of C# Dictionary<K,V>. They have built-in literal syntax, delete(), and a two-value access for checking key existence.
var d = new Dictionary<string, int>();
d["x"] = 1;
bool has = d.ContainsKey("x");
d.Remove("x");m := map[string]int{"x": 1, "y": 2}
m["z"] = 3
// Two-value access (like TryGetValue)
val, ok := m["x"]
if ok {
fmt.Println(val)
}
delete(m, "x") // remove key
// Iterate
for key, val := range m {
fmt.Println(key, val)
}Common Mistakes
When coming from C#, developers often make these mistakes:
- Go has no LINQ equivalent — use explicit for loops
- This is intentional: Go values readability and explicitness over cleverness
- Go 1.23 'slices' and 'maps' packages add some helpers (slices.Contains, slices.Max)
Key Takeaways
- Go has no LINQ — write explicit for loops (intentional design choice)
- range replaces foreach and yields index + value
- append grows slices — always reassign the result
- Two-value map access 'v, ok := m[key]' is the safe equivalent of TryGetValue