C# equivalent of rotating a list using python slice operation

Question:

In python, I can take a list my_list and rotate the contents:

>>> my_list = list(range(10))
>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> new_list = my_list[1:] + my_list[:1]
>>> new_list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

What’s the equivalent way in C# to create a new list that is a made up of two slices of an existing C# list? I know I can generate by brute force if necessary.

Asked By: hughdbrown

||

Answers:

You can easily use LINQ to do this:

// Create the list
int[] my_list = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

IEnumerable<int> new_list =
    my_list.Skip(1).Concat(my_list.Take(1));

You could even add this as an extension method like so:

public static IEnumerable<T> Slice<T>(this IEnumerable<T> e, int count)
{
     // Skip the first number of elements, and then take that same number of
     // elements from the beginning.
     return e.Skip(count).Concat(e.Take(count));
}

Of course there needs to be some error checking in the above, but that’s the general premise.


Thinking about this more, there are definite improvements that can be made to this algorithm which would improve performance.

You can definitely take advantage if the IEnumerable<T> instance implements IList<T> or is an array, taking advantage of the fact that it is indexed.

Also, you can cut down on the number of iterations that are required to skip and take would take within the body of the message.

For example, if you have 200 items and you want to slice with a value of 199, then it requires 199 (for the initial skip) + 1 (for the remaining item) + 199 (for the take) iterations in the body of the Slice method. This can be cut down by iterating through the list once, storing the items in a list which is then concatenated to itself (requiring no iteration).

In this case, the trade off here is memory.

To that end, I propose the following for the extension method:

public static IEnumerable<T> Slice<T>(this IEnumerable<T> source, int count)
{
    // If the enumeration is null, throw an exception.
    if (source == null) throw new ArgumentNullException("source");

    // Validate count.
    if (count < 0) throw new ArgumentOutOfRangeException("count", 
        "The count property must be a non-negative number.");

    // Short circuit, if the count is 0, just return the enumeration.
    if (count == 0) return source;

    // Is this an array?  If so, then take advantage of the fact it
    // is index based.
    if (source.GetType().IsArray)
    {
        // Return the array slice.
        return SliceArray((T[]) source, count);
    }

    // Check to see if it is a list.
    if (source is IList<T>)
    {
        // Return the list slice.
        return SliceList ((IList<T>) source);
    }

    // Slice everything else.
    return SliceEverything(source, count);
}

private static IEnumerable<T> SliceArray<T>(T[] arr, int count)
{
     // Error checking has been done, but use diagnostics or code
     // contract checking here.
     Debug.Assert(arr != null);
     Debug.Assert(count > 0);

     // Return from the count to the end of the array.
     for (int index = count; index < arr.Length; index++)
     {
          // Return the items at the end.
          yield return arr[index];
     }

     // Get the items at the beginning.
     for (int index = 0; index < count; index++)
     {
          // Return the items from the beginning.
          yield return arr[index];          
     }
}

private static IEnumerable<T> SliceList<T>(IList<T> list, int count)
{
     // Error checking has been done, but use diagnostics or code
     // contract checking here.
     Debug.Assert(list != null);
     Debug.Assert(count > 0);

     // Return from the count to the end of the list.
     for (int index = count; index < list.Count; index++)
     {
          // Return the items at the end.
          yield return list[index];
     }

     // Get the items at the beginning.
     for (int index = 0; index < list.Count; index++)
     {
          // Return the items from the beginning.
          yield return list[index];          
     }
}

// Helps with storing the sliced items.
internal class SliceHelper<T> : IEnumerable<T>
{
    // Creates a
    internal SliceHelper(IEnumerable<T> source, int count)
    {
        // Test assertions.
        Debug.Assert(source != null);
        Debug.Assert(count > 0);

        // Set up the backing store for the list of items
        // that are skipped.
        skippedItems = new List<T>(count);

        // Set the count and the source.
        this.count = count;
        this.source = source;
    }

    // The source.
    IEnumerable<T> source;

    // The count of items to slice.
    private int count;

    // The list of items that were skipped.
    private IList<T> skippedItems;

    // Expose the accessor for the skipped items.
    public IEnumerable<T> SkippedItems { get { return skippedItems; } }

    // Needed to implement IEnumerable<T>.
    // This is not supported.
    System.Collections.IEnumerator 
        System.Collections.IEnumerable.GetEnumerator()
    {
        throw new InvalidOperationException(
            "This operation is not supported.");
    }

    // Skips the items, but stores what is skipped in a list
    // which has capacity already set.
    public IEnumerator<T> GetEnumerator()
    {
        // The number of skipped items.  Set to the count.
        int skipped = count;

        // Cycle through the items.
        foreach (T item in source)
        {
            // If there are items left, store.
            if (skipped > 0)
            {
                // Store the item.
                skippedItems.Add(item);

                // Subtract one.
                skipped--;
            }
            else
            {
                // Yield the item.
                yield return item;
            }
        }
    }
}

private static IEnumerable<T> SliceEverything<T>(
    this IEnumerable<T> source, int count)
{
    // Test assertions.
    Debug.Assert(source != null);
    Debug.Assert(count > 0);

    // Create the helper.
    SliceHelper<T> helper = new SliceHelper<T>(
        source, count);

    // Return the helper concatenated with the skipped
    // items.
    return helper.Concat(helper.SkippedItems);
}
Answered By: casperOne
List<int> list1;

List<int> list2 = new List<int>(list1);

or you can

list2.AddRange(list1);

To get a distinct list using LINQ

List<int> distinceList = list2.Distinct<int>().ToList<int>();
Answered By: David Basarab

The closest thing in C# would be to use the Enumerable.Skip and Enumerable.Take extension methods. You could use these to build your new list.

Answered By: Reed Copsey
var newlist = oldlist.Skip(1).Concat(oldlist.Take(1));
Answered By: Joel Coehoorn

To rotate array, do a.Slice(1, null).Concat(a.Slice(null, 1)).

Here’s my stab at it. a.Slice(step: -1) gives a reversed copy as a[::-1].

/// <summary>
/// Slice an array as Python.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="start">start index.</param>
/// <param name="end">end index.</param>
/// <param name="step">step</param>
/// <returns></returns>
/// <remarks>
/// http://docs.python.org/2/tutorial/introduction.html#strings
///      +---+---+---+---+---+
///      | H | e | l | p | A |
///      +---+---+---+---+---+
///      0   1   2   3   4   5
/// -6  -5  -4  -3  -2  -1    
/// </remarks>
public static IEnumerable<T> Slice<T>(this T[] array,
    int? start = null, int? end = null, int step = 1)
{
    array.NullArgumentCheck("array");
    // step
    if (step == 0)
    {
        // handle gracefully
        yield break;
    }
    // step > 0
    int _start = 0;
    int _end = array.Length;
    // step < 0
    if (step < 0)
    {
        _start = -1;
        _end = -array.Length - 1;
    }
    // inputs
    _start = start ?? _start;
    _end = end ?? _end;
    // get positive index for given index
    Func<int, int, int> toPositiveIndex = (int index, int length) =>
    {
        return index >= 0 ? index : index + length;
    };
    // start
    if (_start < -array.Length || _start >= array.Length)
    {
        yield break;
    }
    _start = toPositiveIndex(_start, array.Length);
    // end
    if (_end < -array.Length - 1)
    {
        yield break;
    }
    if (_end > array.Length)
    {
        _end = array.Length;
    }
    _end = toPositiveIndex(_end, array.Length);
    // slice
    if (step > 0)
    {
        // start, end
        if (_start > _end)
        {
            yield break;
        }
        for (int i = _start; i < _end; i += step)
        {
            yield return array[i];
        }
    }
    else
    {
        // start, end
        if (_end > _start)
        {
            yield break;
        }
        for (int i = _start; i > _end; i += step)
        {
            yield return array[i];
        }
    }
}

nunit tests:

[Test]
// normal cases
[TestCase(3, 5, 1, 3, 4)]
[TestCase(0, 5, 1, 0, 4)]
[TestCase(3, null, 1, 3, 9)]
[TestCase(0, null, 1, 0, 9)]
[TestCase(null, null, 1, 0, 9)]
[TestCase(0, 10, 1, 0, 9)]
[TestCase(0, int.MaxValue, 1, 0, 9)]
[TestCase(-1, null, 1, 9, 9)]
[TestCase(-2, null, 1, 8, 9)]
[TestCase(0, -2, 1, 0, 7)]
// corner cases
[TestCase(0, 0, 1, null, null)]
[TestCase(3, 5, 2, 3, 3)]
[TestCase(3, 6, 2, 3, 5)]
[TestCase(100, int.MaxValue, 1, null, null)]
[TestCase(int.MaxValue, 1, 1, null, null)]
[TestCase(-11, int.MaxValue, 1, null, null)]
[TestCase(-6, -5, 1, 4, 4)]
[TestCase(-5, -6, 1, null, null)]
[TestCase(-5, -5, 1, null, null)]
[TestCase(0, -10, 1, null, null)]
[TestCase(0, -11, 1, null, null)]
[TestCase(null, null, 100, 0, 0)]
// -ve step
[TestCase(null, null, -1, 9, 0)]
[TestCase(-7, -5, -1, null, null)]
[TestCase(-5, -7, -1, 5, 4)]
[TestCase(-5, -7, -2, 5, 5)]
[TestCase(-7, null, -1, 3, 0)]
public void Slice01(int? s, int? e, int i, int? first, int? last)
{
    var a = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    var slice = a.Slice(start: s, end: e, step: i).ToArray();
    Print(slice);
    if (first.HasValue)
    {
        Assert.AreEqual(first, slice.First());
    }
    if (last.HasValue)
    {
        Assert.AreEqual(last, slice.Last());
    }
}
Answered By: hIpPy

Function

/*
   list      : the list that is to be rotated
   shift     : the number of elements to be shifted
   direction : direction of shift (1 -> left, -1 -> right)/default left
*/
public List<T> Rotate<T>(List<T> list, int shift, int direction = 1)
{
    int j = 0;
    List<T> temp_buffer = new List<T>();

    if(direction == -1) 
    {
        shift = list.Count - shift; 
    }

    for (int i = 0; i < list.Count; i++)
    {
        if (i < shift)
        {
            temp_buffer.Add(list[i]);
        }

        if (i < list.Count - shift)
        {
            list[i] = list[i + shift];
        }
        else
        {
            list[i] = temp_buffer[j];
            j++;
        }
    }
    return list;
}

Usage Example

List<int> list = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9};
int shift = 4;  //enter the number of elements to be shifted

//printing the list before rotating
Console.Write("Beforet: ");
for(int i = 0; i< list.Count; i++)
{
    Console.Write(list[i] + " ");   
}

list = Rotate<int>(list, shift, -1);

//printing the list after rotating
Console.Write("nAfter t: ");
for(int i = 0; i< list.Count; i++)
{
    Console.Write(list[i] + " ");   
}
Answered By: kamal johnson
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.