I recently updated my Twitter tip deck to a new format for 2022. If you don’t already follow me on Twitter, be sure to @buhakmeh. I use GitHub Actions to go through the existing images and pick one according to the current day of the year. You can think of it as a carousel, where I want to be picking the next element constantly. If I reach the end of the collection, I should loop around back to the first item. Seems simple enough, right?

This post will show two implementations for building an IndexAtLooped extension method for the IList interface. I initially wrote one, and the other written by Twitter user @TheOtherBoz. You can see the Code review thread here to see other suggestions.

The Problem

Given you have a collection of elements, you want to go beyond the upper and lower bounds of the group and loop around to the correct item. But, again, it’s much more straightforward to explain with an image.

indexing examples using visual aid

Given we have a collection that contains numbers from 1 to 5, our expected outcomes for index values of 0, 7, and -1 would be 1, 3, and 5 respectively.

A music playlist is a potentially simple use case for this collection. Where you have a list of favorite songs, and you’d like to iterate through the array. Great, now that we know the problem, let’s look at both solutions.

The Solutions

First, let’s start with mine, which works, but as you’ll see later is the implementation I would not use.

public static class EnumerableExtensions
{
    // @buhakmeh
    public static T? IndexAtLooped<T>(this IList<T>? collection, int index)
    {
        if (collection == null || !collection.Any())
            return default;
        
        var total = collection.Count;
        var remainder = index switch {
            /* get the reverse index */
            <0 => (total - Math.Abs(index % total)) % total,
            _  => index % total
        };

        return collection[remainder];
    }   
}

Note, I’m using C# 9 features and nullable reference types. You can modify this code to use older versions of C#. Now, let’s look at the implementation written by @TheOtherBoz.

public static class EnumerableExtensions
{
    // @TheOtherBoz
    public static T? IndexAtLooped<T>(this IList<T>? collection, int index)
    {
        if (!collection?.Any() ?? true) {
            return default;
        }

        var count = collection.Count;
        var indexWrap = (int)(index - count * Math.Floor((double)index / count));        
      
        return collection.ElementAt(indexWrap);
    }     
}

I like the second implementation because it doesn’t distinguish between indexing in the positive or negative direction. It just works! Now let’s see it in action.

// 1 to 10
var items = Enumerable
    .Range(1, 10)
    .ToList();

var indexes = new[] {20, -1, -20, -15, 5, -23};

var results = indexes
    .Select(index => (index, items.IndexAtLooped(index)))
    .ToList();

foreach (var (index, result) in results) {
    Console.WriteLine($"{index, 3}: {result}");        
}

The resulting output is as follows.

 20: 1
 -1: 10
-20: 1
-15: 6
  5: 6
-23: 8

Great!

Conclusion

The IndexAtLooped is a unique indexing method and has limited use in most cases. That said, if you have use for it, then these implementations should work for you. Keep in mind that the extension method extends IList to avoid multiple enumerations. If you need to extend IEnumerable implementations, you might want to determine the count strategy that works for you.

As always, thanks for reading, and be sure to follow me on Twitter and share this post with colleagues.