11

Does code exist, for shifting List elements to left or right by specified amount, in C#?

It is tricky code, it will take some time to write and test special cases, I would rather reuse something if it exists.

Thanks

user2381422
  • 5,645
  • 14
  • 42
  • 56
  • What do you mean shift? Pad strings (list elements) Left/Right? Order a list? – Sayse Aug 12 '13 at 06:44
  • 4
    Can you elaborate on the shifting that you're trying to achieve? Is this "reassign the indexes of all of the elements in the list, such that `list[0]` returns what was `list[1]`, etc" or "for each element *in* the list, perform some shift operation upon it"? – Damien_The_Unbeliever Aug 12 '13 at 06:45
  • http://stackoverflow.com/questions/15133577/linq-list-moving-elements-up-and-down – Dor Cohen Aug 12 '13 at 06:46
  • I think `list.Insert(index, item)` is the way to go – Sandy Aug 12 '13 at 06:47
  • 1
    What list implementation are you using? Linked list can easily be "shifted", shifting an array backed "list" like `List` is rather expensive - but both is actually really simple.. – Sebastian Aug 12 '13 at 06:55
  • Does not matter. I can easily transfer to array or list whatever I like. – user2381422 Aug 12 '13 at 06:57
  • 2
    The definition of shift: "move (data) one or more places to the right or left in a register." Is that not explicitly obvious? He's obviously not talking about a linked list or B-tree or red-black tree or anything else more obviously complicated than a "List" (https://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx) in C#. Y'all need to study more group theory. – devinbost Apr 02 '15 at 15:35
  • @JoePhillips I was commenting on the fact that the OP is asking a simple question about a specific data type in C# that people are making complicated. – devinbost Sep 24 '19 at 18:28

4 Answers4

14

Something like this for shift left...

public static void ShiftLeft<T>(List<T> lst, int shifts)
{
    for (int i = shifts; i < lst.Count; i++)
    {
        lst[i - shifts] = lst[i];
    }

    for (int i = lst.Count - shifts; i < lst.Count; i++)
    {
        lst[i] = default(T);
    }
}

For shift right it's a little more tricky, because we must copy in reverse

public static void ShiftRight<T>(List<T> lst, int shifts)
{
    for (int i = lst.Count - shifts - 1; i >= 0; i--)
    {
        lst[i + shifts] = lst[i];
    }

    for (int i = 0; i < shifts; i++)
    {
        lst[i] = default(T);
    }
}

With arrays it's a lot more simple, because Array has very powerful methods:

public static void ShiftLeft<T>(T[] arr, int shifts)
{
    Array.Copy(arr, shifts, arr, 0, arr.Length - shifts);
    Array.Clear(arr, arr.Length - shifts, shifts);
}

public static void ShiftRight<T>(T[] arr, int shifts)
{
    Array.Copy(arr, 0, arr, shifts, arr.Length - shifts);
    Array.Clear(arr, 0, shifts);
}

And yes, Array.Copy is protected against overleap: If sourceArray and destinationArray overlap, this method behaves as if the original values of sourceArray were preserved in a temporary location before destinationArray is overwritten.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • @hvd http://stackoverflow.com/a/2381397/613130 Someone benchmarked them... `Array.Copy` is faster (or at least it was). `Array.ConstrainedCopy` *Guarantees that all changes are undone if the copy does not succeed completely.* I'll say that must have a little cost. – xanatos Aug 12 '13 at 07:20
  • @hvd He tested only on `int?`. Perhaps it depends on the type (you would have to test at least `byte`, `int`, `int?`, `DateTime`, `Guid`, `object`, (random object) to be sure... And in various sizes... 1, 16, 100, 1000, 10000.) In the end probably the difference is nearly 0. – xanatos Aug 12 '13 at 07:27
  • I was going to ask why these were not extension methods, but then I saw the code below. – devinbost Apr 02 '15 at 15:29
  • instead of filling the range [lst.Count - shifts, lst.Count ) with default(T), I'm going to set the list Count to (Count - shifts), and I will leave the Capacity untouched since my list can grow again ! – Aminos Nov 03 '21 at 09:46
  • Argh, it's not like C++, I will use RemoveRange instead ! – Aminos Nov 03 '21 at 11:01
11

Below are a couple of extension methods that will shift the list either right or left. The methods will return a list.

public static class ShiftList
{
    public static List<T> ShiftLeft<T>(this List<T> list, int shiftBy)
    {
        if (list.Count <= shiftBy)
        {
            return list;
        }

        var result = list.GetRange(shiftBy, list.Count-shiftBy);
        result.AddRange(list.GetRange(0,shiftBy));
        return result;
    }

    public static List<T> ShiftRight<T>(this List<T> list, int shiftBy)
    {
        if (list.Count <= shiftBy)
        {
            return list;
        }

        var result = list.GetRange(list.Count - shiftBy, shiftBy);
        result.AddRange(list.GetRange(0, list.Count - shiftBy));
        return result;
    }
}

Here's an example of how to call it.

class Program
{
    static void Main(string[] args)
    {
        List<int> test = Enumerable.Range(0, 10).ToList();
        test = test.ShiftLeft(1);

        PrintList(test);
        Console.WriteLine("");

        PrintList(test.ShiftRight(2));

        Console.ReadLine();
    }

    private static void PrintList(List<int> test)
    {
        for (int i = 0; i < test.Count; i++)
        {
            Console.WriteLine(test[i]);
        }
    }
}
nerdybeardo
  • 4,655
  • 23
  • 32
-1

Keep it simple by taking the first part and second part and flipping them. Same thing but flip other way for the ShiftRight

    public static List<int> ShiftLeft(List<int> a, int d)
    {
        if (a.Count > d)
        {
            var beginingPart = a.GetRange(0, d);
            var remainingPart = a.GetRange(d, a.Count - d);
            return remainingPart.Concat(beginingPart).ToList();
        }
        else if (a.Count < d)
        {
            var mod = d % a.Count;
            if (mod != 0)
            {
                return rotLeft(a, mod);
            }
        }

        return a;
    }
conterio
  • 1,087
  • 2
  • 12
  • 25
  • I don't see a similar code snippet, can you tell me which user has similar? Also I'm not sure what question 2 is, I only see one question mark in the op post. If you mean nerdybeardo post, some parts are similar but mine uses recursion and handles an edge case that his doesn't. – conterio Sep 19 '21 at 04:55
-1

Given that "iterations" is the times you want to shift and "numbers" is the List

Shift left:

static void ShiftLeft(int iterations)
    {
        for (int i = 0; i < iterations; i++)
        {
            numbers.Add(numbers[0]);
            numbers.RemoveAt(0);
        }
    }

ShiftRight:

static void ShiftRight(int iterations)
    {
        for (int i = 0; i < iterations; i++)
        {
            numbers.Insert(0, numbers[numbers.Count - 1]);
            numbers.RemoveAt(numbers.Count - 1);
        }
    }
ivanpop
  • 27
  • 2
  • 6
  • This is simple code but have you considered the performance implications? Each call to `RemoveAt()`/`Insert()` causes the rest of the list to be shifted 1 element, and you're passing the worst case, index `0`. If `iterations` is `1000` then instead of shifting each element by 1,000 positions 1 time this will shift each element by 1 position 1,000 times; more shifts are expensive, bigger shifts are not/free. Also, if `numbers.Count == numbers.Capacity` this will cause the internal array to be resized (doubled) just to hold that one extra element that exists before `RemoveAt()` is called. – Lance U. Matthews Jun 18 '22 at 18:29