Recently, I learned Kotlin to develop an Android mobile application; my software development experience is primarily in C#. Throughout the learning process, I searched the internet for "Kotlin equivalent of [insert C# feature name]." The search proved to be an inefficient way to learn Kotlin. After all, with my C# experience, I knew many core programming concepts and needed to translate my existing knowledge into Kotlin code.

My goal for this article is to enable C# developers to learn Kotlin easily by having one place to look for the equivalent C# feature in Kotlin. Since comparing every aspect of C# to Kotlin would be lengthy, this article focuses on comparing Language-Integrated Query (LINQ) methods in C# to their equivalent Kotlin collection function. 

LINQ methods are a central part of C#. If you have a data collection in code, you will likely use LINQ to process that data. Focusing on LINQ first will provide the most value for the C# developer who wants to learn Kotlin.

In this article, I review some of the most common LINQ methods and their equivalent function in Kotlin. A complete reference list of LINQ methods and their equivalent Kotlin collection functions is at the end.

Employee class example

Throughout this article, Employee class is used for the C# examples:

public class Employee
{
    public string Name { get; set; }
    public int YearsWithCompany { get; set; }
}

This Employee class has two properties: a string (text) property for the employee's name and an int (number) property for the years the employee has been with the company.

Use this equivalent class for the Kotlin examples:

data class Employee(val name: String, val yearsWithCompany: Int)

Use this list of employees for the C# examples:

var employees = new List<Employee>
{
    new() { Name = "Tom", YearsWithCompany = 3 },
    new() { Name = "Jane", YearsWithCompany = 6 }
};

Use this collection for the Kotlin examples:

val employees = listOf(Employee("Tom", 3), Employee("Jane", 6))

Filtering items based on a condition

One common LINQ method is Where(). This method filters items in a collection that pass a given condition. An example of the Where() method:

var seniorEmployees = employees.Where(employee => employee.YearsWithCompany > 5).ToList();

This example shows a collection of Employee objects with a name field and a "years with the company" field. The Where() method filters the collection of objects by only employees that have been with the company for more than five years. The filtered employees are stored in the variable called seniorEmployees. The seniorEmployees collection has one employee: Jane, who has been with the company for six years.

At the end of the example is the ToList() method call. This call is necessary because the Where() method does not return the expected collection of data (a collection of Employee objects). It returns an object that has instructions to iterate over the initial collection to get the expected collection. Calling ToList() runs what is known as deferred execution: you can get instructions via a LINQ method but then delay executing the instructions until necessary. The benefit of deferred execution is to increase code performance by chaining multiple LINQ methods and calling ToList() once at the end to produce a single output collection.

The equivalent Kotlin function is filter(). This is how you would use the filter() function to implement the same logic as in the C# example:

val seniorEmployees = employees.filter { it.yearsWithCompany > 5 }

In this Kotlin example, there is a collection of Employee objects like the C# example. The filter() function filters the collection on the same condition as the C# example. (The it keyword is an implicit variable representing an item in the collection.) Finally, the collection of filtered employees is stored in the variable called seniorEmployees. It has the same employee, Jane, who has been with the company for six years.

Transforming each item in a collection

Another common LINQ method is  Select(). The method transforms each item in a collection into another type using the specified selector function parameter. Here is an example of the method:

var employeeNames = employees.Select(employee => employee.Name).ToList();

In this example, the Select() method transforms each Employee object in the collection into a new type by selecting the Name property. The resulting collection, employeeNames, is a list of strings containing each employee's name.

The Kotlin equivalent of Select() is map():

val employeeNames = employees.map { it.name }

In this example, the map() function transforms or maps the list of employees to the name property and returns a list of strings like the C# example.

Returning the first item in a collection

The LINQ method FirstOrDefault() in C# returns the first item in a collection or a default value if there are no items in the collection. An example:

var firstEmployeeInList = employees.FirstOrDefault();

In this example, the FirstOrDefault() method gets the first Employee object in the employees collection. Then the first Employee object is assigned to the variable firstEmployeeInList. The first employee is Tom.

The Kotlin equivalent function is firstOrNull():

val firstEmployeeInList = employees.firstOrNull()

In this Kotlin example, the firstOrNull() function gets the first Employee object, like the C# example. The first employee is again Tom.

There is a useful overload of FirstOrDefault() that takes a condition predicate parameter. This overload is like the Where() method but only returns the first item that passes the condition. In Kotlin, use the firstOrNull() function since it also has an overload that takes a condition predicate parameter.

Sorting items in a collection

The LINQ method OrderBy() in C# sorts the items in a collection in ascending order using the key specified in the keySelector parameter. An example of OrderBy():

var sortedEmployees = employees.OrderBy(employee => employee.Name).ToList();

In this example, the OrderBy() method sorts the items in the employees collection by name, using the Employee object's Name property as the key. The employees in the returned collection, sortedEmployees, are in the order "Jane" and "Tom."

The Kotlin equivalent function is sortedBy():

val sortedEmployees = employees.sortedBy { it.name }

In this example, the sortedBy() function sorts the items in the employees collection by the employee name, like the C# example. The sortedBy() function takes a selector function parameter, like the OrderBy() keySelector function parameter in C#.

The LINQ method OrderByDescending() sorts the items in descending order. The Kotlin equivalent function is sortedByDescending().

Full LINQ to Kotlin collections reference

Below is an exhaustive list of LINQ methods and their equivalent Kotlin collection functions. In some cases, there is no equivalent Kotlin function.

The LINQ method list is based on the .NET 7 version. For more information on each LINQ method, go to the Microsoft documentation.

For more information on each Kotlin collection function, go to this documentation.

C# LINQKotlin collections
Aggregatereduce
Allall
Anyany
Appendplus, addAll
AsEnumerableasIterable
Averageaverage
Cast------------
Chunkchunked
Concatplus, addAll
Containscontains, containsAll
Countcount
DefaultIfEmpty------------
Distinctdistinct
DistinctBydistinctBy
ElementAtelementAt
ElementAtOrDefaultelementAtOrElse, getOrNull, getOrElse
EmptyemptyList
Exceptsubtract
ExceptBy------------
Firstfirst
FirstOrDefaultfirstOrNull, find
GroupBygroupBy
GroupJoin------------
Intersectintersect
IntersectBy------------
Join------------
Lastlast
LastOrDefaultlastOrNull, findLast
LongCountcount
MaxmaxOrNull
MaxBymaxByOrNull
MinminOrNull
MinByminByOrNull
OfTypefilterIsInstance
Ordersort
OrderBysortBy, sortedBy
OrderByDescendingsortByDescending, sortedByDescending
OrderDescendingsortDescending
Prepend------------
Range------------ Use x..y syntax to iterate over a range instead. (C# has range syntax too.)
Repeat------------ Use collection constructor instead.
Reversereversed, asReversed
Selectmap
SelectManyflatMap, flatten
SequenceEqualcontentEquals, contentDeepEquals
Singlesingle
SingleOrDefaultsingleOrNull
Skipdrop
SkipLastdropLast
SkipWhiledropWhile
Sumsum, sumOf
Taketake
TakeLasttakeLast
TakeWhiletakeWhile
ThenBy------------ Use thenBy in kotlin.comparisons library.
ThenByDescending------------ Use thenByDescending in kotlin.comparisons library.
ToArray------------
ToDictionaryassociate, associateBy
ToHashSettoHashSet
ToListasList
ToLookup------------
TryGetNonEnumeratedCount------------
Unionunion
UnionBy------------
Wherefilter
Zipzip

Technologies