Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.
So last week we covered the Enumerable.Range() method in the System.Linq namespace, which gives us a handy way to generate a sequence of integers to either use directly, or to feed into a more complex expression. Today we’re going to look at another static method called Enumerable.Repeat() that allows us to repeat an element the specified number of times.
Using Repeat() to generate a sequence of a repeated value
Again, if we take a peek into the Enumerable static class in the System.Linq namespace, we see the vast array of LINQ extension methods, as well as a few static methods. One of which is a method called Repeat() whose purpose is to generate a sequence of repeated occurrences of a given element.
It’s syntax looks like this:
- Repeat(element, count)
- Repeats the given element for the specified count.
So given this, if we wanted to create an array of 10 occurrences of the same string, we could do so by coding:
1 : // Creates a sequence of 5 occurrences of "Hello World!" and then stores in
// array
2 : var repeatedValues = Enumerable.Repeat("Hello World!", 5).ToArray();
The main thing to notice in Repeat() is that it repeats the given element, it does not (at least not directly) repeatedly call an expression to generate multiple separate values.
For example, consider the following code:
1: var ran = new Random();
2:
3: // does this generate 100 random numers?
4: // Or repeat first random number 100 times?
5: var repeatedRandom = Enumerable.Repeat(ran.Next(), 100);
What do we get? Do we get a sequence of 100 random numbers? Or a sequence of the first random number repeated 100 times?
The answer is the latter. The ran.Next() is resolved to get the value of the parameter, and then that result is the element that is repeated 100 times. That is, if the first random number generated was 103242, then the array would contain { 103242, 103242, 103242, …, 103242 }.
So, we can repeat a single element several times, right? Obviously if that’s what you needed to do then it’s right up your alley, but once again if we look past the simple examples, we see some more interesting other things that this also enables us to do.
Using Repeat() to turn a single item into a sequence of one item
One of the nice things about Repeat() is it allows us to easily represent a single item as a sequence of a single item. For example, if you have a method that takes in an IEnumerable<T>, but you only have one T to give it, Repeat() makes it easy to pass in a sequence of length 1.
For example, if we had this method that takes an IEnumerabe<T> sequence of string:
1: // method that takes a sequence of string
2: public void AddElements(IEnumerable<string> elementSequence)
3: {
4: // ...
5: }
But we just wanted to pass the single string “EOF” into the method, we can use Repeat() to do so:
1: // makes "EOF" into a sequence of 1 containing "EOF"
2: AddElements(Enumerable.Repeat("EOF", 1));
So that’s a handy feature, though we could also, of course, do this by creating an explicit string[1] array or a List<string> as well. In fact I have a blog post on Returning Zero or One Item As IEnumerable<T> (here) where I talk about this in more detail, including weighing the performance and mutability ramifications.
We could also create a sequence of size length 0, though this is a less interesting use of Repeat() as the element value would be unused and the Enumerable.Empty<T singleton generator is more efficient if we know the sequence is intended to be empty (again, see the above mentioned blog post for more details).
Using Repeat() to repeat a generator to create sequences
We said before that Repeat() can be used to repeat a given element a specific number of times. This makes it sound like this can only be used to generate sequences of a repeated value. While this is generally true when you consider Repeat() by itself, this is not necessarily true if you think beyond the element representing a simple value.
For example, let’s revisit the idea of creating a sequence of 100 random numbers, except this time, we will make the element a Func<int> that returns a random number. So we will repeat the Func<int> 100 times, well wait, how does that help us? Won’t we just get the same reference to the delegate repeated 100 times? Well yes, but that’s not where we’ll end, we’ll then use Select() to invoke that delegate and return the result, giving us 100 random numbers:
1: var ran = new Random();
2:
3: // create a delegate from the method group,
4: // repeat that delegate 100 times,
5: // for each delegate, project it to its result,
6: // convert to array.
7: var results = Enumerable.Repeat<Func<int>>(ran.Next, 100)
8: .Select(f => f())
9: .ToArray();
Notice that we have to tell Repeat() that the type of element is a Func<int>, this is because it’s harder for the compiler to correctly infer the type Func<int> from a method group or delegate. Also notice our element is ran.Next which is a method group and roughly equivalent to the lambda () => ran.Next().
So, that delegate reference is in a sequence 100 times, and for each item, the Select() projects it to the result from executing the delegate, which gives us a nice sequence of 100 random values.
Note that we could have gotten a similar result by using Enumerable.Range() as follows:
1: var ran = new Random();
2:
3: // create a sequence from 0 to 999 (1000 items),
4: // for each int, project to the next random number,
5: // convert to array
6: var results = Enumerable.Range(0, 1000)
7: .Select(i => ran.Next())
8: .ToArray();
So in this method, we create a sequence of 1000 items (from 0 to 999) which we really don’t want to use, we’re more just using them to drive the Select() projection to return 1000 random numbers. For more information on Range() see last week’s post on The Enumerable.Range() Static Method (here).
Both of these are similar ways to get the same results. Some may prefer the first because Repeat() seems a more natural idiom than Range() for repeating a delegate, but it does have the baggage of creating two delegates (one for the generator, one for the projection) instead of the one for Range() (only requires the one for projection).
Which is better? Execution-wise, the use of Range above is a hair faster (but really, the difference is very minor and of course that’s subject to change as the framework changes). Other than that, it’s really up to you which way you prefer as they both accomplish the same goal.
Summary
The Enumerable.Repeat() method performs the simple task of creating a sequence by repeating an element a specific number of times. While this is, in of itself, a trivial need, it can also be used to drive more useful results such as repeating a generator delegate, or creating sequences out of single items.
Print | posted on Thursday, May 3, 2012 7:21 PM | Filed Under [ My BlogC#Software.NETLittle Wonders ]