C#
GO

C# to Go

10 lessons

Progress0%
1Introduction2Type System3Classes to Structs4Interfaces5Error Handling6Async to Goroutines7Generics8LINQ to Slices9Testing10Packages and Modules
All Mirror Courses
C#
GO
LINQ to Slices
MirrorLesson 8 of 10
Lesson 8

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.

Mirror Card
C#
From C#:

In C#, you're familiar with linq to slices.

GO
In Go:

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:

GO
Go 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#:

C#
C# (What you know)
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);
Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Go has no LINQ equivalent — use explicit for loops

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

This is intentional: Go values readability and explicitness over cleverness

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

Go 1.23 'slices' and 'maps' packages add some helpers (slices.Contains, slices.Max)

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

range iterates over slices, maps, strings, and channels — like C# foreach

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

GO
In Go:

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.

C#
C#
var list = new List<int> { 1, 2, 3 };
list.Add(4);
int first = list[0];
int n = list.Count;
GO
Go
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 array
Common Pitfall
append may or may not allocate a new array. Always use the returned value: nums = append(nums, x). Discarding the return value is a bug.

2. 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.

C#
C#
foreach (var item in list) {
    Console.WriteLine(item);
}
foreach (var (key, val) in dict) {
    Console.WriteLine($"{key}: {val}");
}
GO
Go
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.

C#
C#
var filtered = list.Where(x => x > 2).ToList();
var mapped   = list.Select(x => x * x).ToList();
GO
Go
// 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.

C#
C#
var d = new Dictionary<string, int>();
d["x"] = 1;
bool has = d.ContainsKey("x");
d.Remove("x");
GO
Go
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)
}
Rule of Thumb
Always use the two-value map access 'v, ok := m[key]' when the key might be absent. Accessing a missing key returns the zero value silently.

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)
Common Pitfall
Don't assume Go works exactly like C#. While the concepts may be similar, the syntax and behavior can differ significantly.

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
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C# code in Go to practice these concepts.
PreviousNext