Python: using LINQ operators

In this tutorial we are going to learn how to use the py-linq module, which brings the LINQ operators to Python.

Introduction

In this tutorial we are going to learn how to use the py-linq module, which brings the LINQ operators to Python.

LINQ is a library used to execute queries in C# syntax against many different types of data [1], which includes arrays and lists. LINQ allows to make code much simpler, elegant and compact and allows to write operations that would typically be implemented in a loop in a shorter syntax.

The easiest way to install py-linq is using pip (a package installer). To do it, simply send the following command:

pip install py-linq

This tutorial was tested on Python 3.7.2 and on Windows 8.1.

The code

We will start by importing the Enumerable class from the previously installed module.

from py_linq import Enumerable

Then we will instantiate an object of the Enumerable class. We will apply the LINQ operators over this object.

Note that, when we apply LINQ operators, a new Enumerable object will be returned and the original collection won’t be mutated. This means we can use this same Enumerable to test multiple operators without affecting the results.

As input of the constructor we will pass a list of integers. However, the constructor can receive as input any collection as long as it implements the __iter__ dunder [2].

In this introductory tutorial we are going to cover very simple use cases, which is why we are using a list of integers. Nonetheless we could pass instead a collection of objects, which allows tackle much more realistic and useful use cases.

ints = Enumerable([1,2,3,4,5,6,7,8,9])

The first thing we are going to test is how to filter our sequence of integers. For testing purposes, we are going to filter the original sequence to get a new Enumerable that contains only even numbers.

To perform the filtering of the elements, we need to use the where method. As input, this method receives a function (called predicate) that will be applied to each element of the Enumerable and will test the filtering condition.

So, if the filtering condition applies for the given element, the function should return true. Otherwise it should return false.

Since, as mentioned, the function will be applied for each element of the Enumerable, for our use case it is expected that the function argument will be an integer.

Although we can use a named function as predicate, it is very common that we use a lambda function instead. This the common approach because, typically, predicates are not reusable functions and the code is easier to read if the implementing function is defined inline as argument of the LINQ method.

For an introduction on lambda functions and their syntax you can check this awesome article. In short, the syntax is the following:

lambda args: expression

So, after the lambda keyword, we specify the arguments and then after “:” we put the expression for our function.

In our case we only want to check if the number is even. This is easily implemented by checking if the remainder of the division by 2 is equal to 0. If it is, then the number is even.

even = ints.where(lambda x: x % 2 == 0)

As mentioned before, the result of this operation is a new Enumerable, which we can directly print to the prompt.

print(even)

After this we will check three other basic LINQ operators: min, max and count. These allow to get the min and max elements of the Enumerable and the total number of elements of the Enumerable, respectively.

These methods return as output an integer with the result, which we can directly print to the prompt.

print(ints.min())

print(ints.max())

print(ints.count())

As a final example we will check a very common use case when using LINQ: chaining operators to create more complex expression trees. For example, after filtering a sequence, we may want to order it.

In our example we are going to filter our sequence to get all the elements greater than 4, order it in descending order and then get the first element.

So, like before, we start by using the where operator and pass as input a lambda function that returns true if the element is greater than 4 and false otherwise.

Then we will call the order_by_descending method, which will order the sequence in descending order.

Finally, to get the first element of the resulting sequence, we call the first method.

Note that, after getting the numbers greater than 4 we obtain [5,6,7,8,9]. Then, after ordering it in descending order, we get [9,8,7,6,5]. Thus, the first element of this final sequence is 9.

result = ints.where(lambda x: x > 4) \
    .order_by_descending(lambda x: x) \
    .first()

print(result)

The complete code can be seen below. We have added some extra prints for better readability.

from py_linq import Enumerable

ints = Enumerable([1,2,3,4,5,6,7,8,9])

even = ints.where(lambda x: x % 2 == 0)
print("Even numbers:")
print(even)

print("\nMin:")
print(ints.min())

print("\nMax:")
print(ints.max())

print("\nCount:")
print(ints.count())


result = ints.where(lambda x: x > 4) \
    .order_by_descending(lambda x: x) \
    .first()

print("\nChained expression result:")
print(result)

Testing the code

To test the code, simply run it in a tool of your choice. In my case I’ve used IDLE, a Python IDE.

You should obtain a result similar to figure 1. As can be seen, in the first use case, we have obtained a sequence containing all the even numbers that were on the original sequence.

Then, we have obtained the minimum value (1) the maximum value (9) and the total number of elements (9), which are correct.

To finalize, after applying the whole expression, we have obtained the number 9, which is also correct.

Output of the program.
Figure 1 – Output of the program.

References

[1] https://www.codingame.com/playgrounds/213/using-c-linq—a-practical-overview/introduction

[2] https://viralogic.github.io/py-enumerable/

Leave a Reply

%d bloggers like this: