Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enumerable.Range - When does it make sense to use?

Tags:

When programming it's almost instinctive deciding when to use a for loop, or foreach, but what is the determining factors or problem space for choosing to use Enumerable.Range?

A For Loop is chosen when we want to iterate a set number of times (over a simple data type) to calculate/do a repetitive task.

A For Each is similar, but chosen when we want to iterate over a list of complex objects to calculate/do a repetitive task.

Again, what's the determining factors for using an Enumerable.Range?

IEnumerable<int> squares = Enumerable.Range(4, 3).Select(x => x * x);
like image 200
Dane Balia Avatar asked Jun 20 '14 06:06

Dane Balia


People also ask

What does enumerable range do?

Enumerable Range Generates a sequence of integral numbers within a specified range.

What is enumerable in C#?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.

What is enumerable repeat in C#?

Enumerable. Repeat method is part of System. Linq namespace. It returns a collection with repeated elements in C#. Firstly, set which element you want to repeat and how many times.

How do you iterate through a range in C#?

To program a range loop in C# we use the Range() LINQ extension method. That method makes an enumerable ( IEnumerable ) with a range of integer values (Microsoft Docs, n.d. a; Stephens, 2014). We then loop over that enumerable to implement the range loop. To access that method we use the static Enumerable class.


2 Answers

foreach is about iterating over an existing set/collection.

Enumerable.Range is for generating a set/collection. You wouldn't, generally, want to write a for loop just to generate a set if it can be generated by Enumerable.Range - you'd just be writing boilerplate code that's longer and requires you to allocate some kind of storage (e.g. a List<int>) to populate first.

like image 176
Damien_The_Unbeliever Avatar answered Sep 19 '22 03:09

Damien_The_Unbeliever


As mentioned, Enumerable.Range isn't directed at looping, but rather creating the range. This makes one liners in Linq possible without the need of creating subsets. One additional advantage of that power is, you could even generate a subrange within a sub statement, something that is not always possible with a for and lambda's, because yield is not possible inside lambdas.

For example, a SelectMany could also use an Enumerable.Range. Test collection:

class House
{
    public string Name { get; set; }
    public int Rooms;
}

    var houses = new List<House>
    {
        new House{Name = "Condo", Rooms = 3},
        new House{Name = "Villa", Rooms = 10}
    };

The example on itself doesn't hold much value of course, but for getting all the rooms, the implementation in Linq would be:

   var roomsLinq = houses.SelectMany(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));

With iteration, it would require 2 iterations:

   var roomsIterate = new List<string>();
    foreach (var h in houses)
    {
        for (int i = 1; i < h.Rooms + 1; i++)
        {
            roomsIterate.Add(h.Name + ", room " + i);
        }
    }

You could still say, the 2nd code is more readable, but that boils down to using Linq or not in general.


So, one step further, we want a IEnumerable<IEnumerable<string>> of all the rooms (a string enumerable of rooms per house).

Linq:

listrooms = houses.Select(h => Enumerable.Range(1, h.Rooms).Select(i => h.Name + ", room " + i));

But now, we would need 2 collections when using iteration:

    var list = new List<IEnumerable<string>>();
    foreach (var h in houses)
    {
        var rooms = new List<string>();
        for (int i = 1; i < h.Rooms + 1; i++)
        {
            rooms.Add(h.Name + ", room " + i);
        }
        list.Add(rooms);
    }

Another scenario, imo one of the great things about Linqs and lambdas, is that you can use them as parameters (e.g. for injections purposes), which is made possible in an easier way with Enumerable.Range.

For example, you have a function, that takes a parameter roomgenerator

static IEnumerable<Furniture> CreateFurniture(Func<House,IEnumerable<string>> roomgenerator){
   //some house fetching code on which the roomgenerator is used, but only the first 4 rooms are used, so not the entire collection is used.
}

The rooms iteration above could be returned with Enumerable.Range, but with iteration either a sub collection for rooms must be created first, or a separate function that yields the results. The subcollection would have the great disadvantage that it is always populated completely, even if only one item is needed from the enumeration. The separate method is often overkill, since it is only needed for a single parameter use, hence Enumerable.Range can save the day.

like image 26
Me.Name Avatar answered Sep 22 '22 03:09

Me.Name