C# equivalent of rotating a list using python slice operation

后端 未结 6 1726
借酒劲吻你
借酒劲吻你 2020-12-14 06:52

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]
>         


        
相关标签:
6条回答
  • 2020-12-14 07:09
    var newlist = oldlist.Skip(1).Concat(oldlist.Take(1));
    
    0 讨论(0)
  • 2020-12-14 07:09

    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);
    }
    
    0 讨论(0)
  • 2020-12-14 07:13

    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("Before\t: ");
    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] + " ");   
    }
    
    0 讨论(0)
  • 2020-12-14 07:15
    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>();
    
    0 讨论(0)
  • 2020-12-14 07:17

    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());
        }
    }
    
    0 讨论(0)
  • 2020-12-14 07:27

    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.

    0 讨论(0)
提交回复
热议问题