Featured image

Enumerable.Range by example

C# Enumerable.Range function generates sequence of numbers. This article explores details of the Range function as well as its use cases.

Enumerable.Range overview

It takes two arguments. First argument is the first number in sequence (e.g. 10, means that the first number in sequence is 10). Second argument is the number of items in sequence (e.g. 11 means that it will return 11 numbers).

It returns IEnumerable<int> which contains generated sequence. Keep in mind that each subsequent number is incremented by 1, which also means that it’s in ascending order. This however can be changed with some Linq tricks (check examples below).

Range function works only with integral numbers, but with the help of Linq we can make it work with floats.

Basic example

Lets start with basic example. We’re going to print numbers 9 to 99.

// print 91 numbers, 9 being the first number
foreach(var number in Enumerable.Range(9, 91))
{
    Console.WriteLine(number);
}

Reversing sequence, descending order

Linq makes reversing the sequence very easy. All you need to do is to use Reverse method. Check out following example, which prints number 10 to 1.

// generate sequence of numbers from 1 to 10
// then reverse it
foreach(var number in Enumerable.Range(1, 10).Reverse())
{
    Console.WriteLine(number);
}

Generating sequence of even (or odd) numbers only

This turns out to be pretty easy with modulo and Linq. However, there is a caveat. Code below won’t generate 100 numbers. It is because we firstly generate numbers 1 to 100 then we select only numbers which match criteria (even or odd), which basically halves amount of numbers.

var even =  Enumerable.Range(1, 100).Where(number => number % 2 == 0);
var odd =  Enumerable.Range(1, 100).Where(number => number % 2 == 1);

Negative numbers

Suppose you want to generate sequence of numbers from -1 to -10. The easiest way to do it (at least in terms of Enumerable.Range) is to generate sequence from 1 to 10 and turn each number to negative number.

var numbers =  Enumerable.Range(1, 10).Select(number => -number);

Increment (or decrement) by 2 or 3 or any number

Suppose that you want your sequence step to be 2 instead on 1. For example you want 1, 3, 5, 7, 9 instead of 1, 2, 3, 4, 5.

The tricks is to multiple each number by 2 and then substract 2 from it.

var numbers =  Enumerable.Range(1, 5).Select(number => number * 2 - 2);

If you need your numbers to alter by 3 (or any other number) then you have to replace 2 with 3.

Working with floats

Enumerable.Range doesn’t work with floats. As you’ve seen in previous use cases it generates only integer values. Unless you use Linq, but it has a drawback. It hard to read, in addition you have to think twice before you (or someone else reading your code) figure it out. With that being said, lets take a look at the example.

Following snippet generates sequence of numbers from 1 to 2 with step 0.1.

var numbers =  Enumerable.Range(10, 10).Select(number => number / 10f);
// for each number in sequence 10, 11, 12, ..., 20
// divide it by 10
// which results in sequence 1.0, 1.1, 1.2, ..., 2.0

Custom sequence generator

You’ve probably noticed that some examples are a little bit far fetched. Enumerable.Range works well for simple scenarios. However advanced use cases are hard to read and result in additional Linq overhead. Fortunately we can easily create our own custom sequence generators.

You can use following technique to rewrite every example in this post. I won’t go into details, so it’s up to you to figure it out, but let me just show you how you can rewrite the even numbers generator.

public static class Sequences
{
    public static IEnumerable<float> Even(int start, int count)
    {
        // validation skipped e.g. count should always be more than 0
        // I'll leave it as an exercise for you

        // if start is odd number then increment it
        // so the first number is always even
        if (start % 2 == 1)
        {
            start++;
        }

        int counter = 0;
        while (counter < count)
        {
            yield return start;
            counter++;
            start += 2;
        }
    }
}

// usage - get 10 even numbers starting with 2
foreach (var number in Sequences.Even(2, 10))
{
    // some action
}