Làm thế nào để lấy tất cả trừ phần tử cuối cùng trong chuỗi bằng LINQ?


131

Hãy nói rằng tôi có một chuỗi.

IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000

Bắt chuỗi không rẻ và được tạo động và tôi chỉ muốn lặp lại qua nó một lần.

Tôi muốn nhận 0 - 999999 (tức là mọi thứ trừ phần tử cuối cùng)

Tôi nhận ra rằng tôi có thể làm một cái gì đó như:

sequence.Take(sequence.Count() - 1);

nhưng điều đó dẫn đến hai bảng liệt kê trong chuỗi lớn.

Có cấu trúc LINQ nào cho phép tôi làm không:

sequence.TakeAllButTheLastElement();

3
Bạn đã chọn giữa thuật toán hiệu quả không gian O (2n) hoặc O (đếm), trong đó thuật toán sau cũng cần di chuyển các mục trong một mảng bên trong.
Dykam

1
Dario, bạn có thể giải thích cho một người không thành công lớn không?
alexn


Tôi đã kết thúc với bộ nhớ đệm bằng cách chuyển đổi bộ sưu tập thành Danh sách và sau đó gọi sequenceList.RemoveAt(sequence.Count - 1);. Trong trường hợp của tôi, điều đó có thể chấp nhận được vì sau tất cả các thao tác LINQ, tôi phải chuyển đổi nó thành mảng hoặc bằng IReadOnlyCollectionmọi cách. Tôi tự hỏi trường hợp sử dụng của bạn là gì khi bạn thậm chí không xem xét bộ nhớ đệm? Như tôi có thể thấy câu trả lời thậm chí đã được phê duyệt thực hiện một số bộ nhớ đệm để chuyển đổi đơn giản thành Listdễ dàng hơn và ngắn hơn theo quan điểm của tôi.
Pavels Ahmadulins

Câu trả lời:


64

Tôi không biết một giải pháp Linq - Nhưng bạn có thể dễ dàng tự mình viết mã thuật toán bằng cách sử dụng các trình tạo (lợi nhuận).

public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source) {
    var it = source.GetEnumerator();
    bool hasRemainingItems = false;
    bool isFirst = true;
    T item = default(T);

    do {
        hasRemainingItems = it.MoveNext();
        if (hasRemainingItems) {
            if (!isFirst) yield return item;
            item = it.Current;
            isFirst = false;
        }
    } while (hasRemainingItems);
}

static void Main(string[] args) {
    var Seq = Enumerable.Range(1, 10);

    Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
    Console.WriteLine(string.Join(", ", Seq.TakeAllButLast().Select(x => x.ToString()).ToArray()));
}

Hoặc như một giải pháp tổng quát loại bỏ n mục cuối cùng (sử dụng hàng đợi như được đề xuất trong các nhận xét):

public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) {
    var  it = source.GetEnumerator();
    bool hasRemainingItems = false;
    var  cache = new Queue<T>(n + 1);

    do {
        if (hasRemainingItems = it.MoveNext()) {
            cache.Enqueue(it.Current);
            if (cache.Count > n)
                yield return cache.Dequeue();
        }
    } while (hasRemainingItems);
}

static void Main(string[] args) {
    var Seq = Enumerable.Range(1, 4);

    Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
    Console.WriteLine(string.Join(", ", Seq.SkipLastN(3).Select(x => x.ToString()).ToArray()));
}

7
Bây giờ bạn có thể khái quát nó để lấy tất cả trừ n cuối cùng không?
Eric Lippert

4
Đẹp. Một lỗi nhỏ; kích thước hàng đợi nên được khởi tạo thành n + 1 vì đó là kích thước tối đa của hàng đợi.
Eric Lippert

ReSharper không hiểu mã của bạn ( gán trong biểu thức có điều kiện ) nhưng tôi khá thích mã đó +1
érégôgôgô

44

Thay thế cho việc tạo phương thức của riêng bạn và trong trường hợp thứ tự các phần tử không quan trọng, phần tiếp theo sẽ hoạt động:

var result = sequence.Reverse().Skip(1);

49
Lưu ý rằng điều này đòi hỏi bạn phải có đủ bộ nhớ để lưu trữ toàn bộ chuỗi và tất nhiên nó VẪN lặp lại toàn bộ chuỗi hai lần, một lần để xây dựng chuỗi đảo ngược và một lần để lặp lại nó. Khá nhiều thứ này tệ hơn giải pháp Count cho dù bạn cắt nó như thế nào; nó chậm hơn và chiếm nhiều bộ nhớ hơn.
Eric Lippert

Tôi không biết phương thức 'Đảo ngược' hoạt động như thế nào, vì vậy tôi không chắc về dung lượng bộ nhớ mà nó sử dụng. Nhưng tôi đồng ý về hai lần lặp. Phương pháp này không nên được sử dụng trên các bộ sưu tập lớn hoặc nếu hiệu suất là quan trọng.
Kamarey

5
Vâng, làm thế nào bạn sẽ thực hiện Reverse? Bạn có thể tìm ra một cách chung để làm điều đó mà không lưu trữ toàn bộ chuỗi?
Eric Lippert

2
Tôi thích nó, và nó đáp ứng tiêu chí không tạo ra chuỗi hai lần.
Amy B

12
và ngoài ra, bạn cũng sẽ cần phải đảo ngược toàn bộ chuỗi một lần nữa để giữ nó vì nó equence.Reverse().Skip(1).Reverse()không phải là một giải pháp tốt
shashwat

42

Bởi vì tôi không phải là người thích sử dụng một cách rõ ràng Enumerator, đây là một sự thay thế. Lưu ý rằng các phương thức trình bao bọc là cần thiết để cho phép các đối số không hợp lệ xuất hiện sớm, thay vì trì hoãn việc kiểm tra cho đến khi chuỗi thực sự được liệt kê.

public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentNullException("source");

    return InternalDropLast(source);
}

private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source)
{
    T buffer = default(T);
    bool buffered = false;

    foreach (T x in source)
    {
        if (buffered)
            yield return buffer;

        buffer = x;
        buffered = true;
    }
}

Theo đề xuất của Eric Lippert, nó dễ dàng khái quát hóa cho n mục:

public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source, int n)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (n < 0)
        throw new ArgumentOutOfRangeException("n", 
            "Argument n should be non-negative.");

    return InternalDropLast(source, n);
}

private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
{
    Queue<T> buffer = new Queue<T>(n + 1);

    foreach (T x in source)
    {
        buffer.Enqueue(x);

        if (buffer.Count == n + 1)
            yield return buffer.Dequeue();
    }
}

Bây giờ tôi đệm trước khi cho năng suất thay vì sau khi cho năng suất, do đó n == 0trường hợp không cần xử lý đặc biệt.


Trong ví dụ đầu tiên, có thể sẽ nhanh hơn một chút để đặt buffered=falsetrong mệnh đề khác trước khi gán buffer. Điều kiện đã được kiểm tra bằng mọi cách, nhưng điều này sẽ tránh thiết lập dự phòng bufferedmỗi lần qua vòng lặp.
James

Ai đó có thể cho tôi biết những ưu / nhược điểm của câu hỏi này so với câu trả lời được chọn không?
Sinjai

Lợi thế của việc thực hiện trong một phương pháp riêng biệt mà thiếu các kiểm tra đầu vào là gì? Ngoài ra, tôi chỉ cần bỏ thực hiện duy nhất và cung cấp cho nhiều triển khai một giá trị mặc định.
jpmc26

@ jpmc26 Với việc kiểm tra theo một phương thức riêng biệt, bạn thực sự có được xác nhận ngay khi bạn gọi DropLast. Mặt khác, xác nhận chỉ xảy ra khi bạn thực sự liệt kê chuỗi (trong lần gọi đầu tiên đến MoveNextkết quả IEnumerator). Không phải là một điều thú vị để gỡ lỗi khi có thể có một lượng mã tùy ý giữa việc tạo IEnumerablevà thực sự liệt kê nó. Ngày nay tôi viết InternalDropLastnhư một chức năng bên trong DropLast, nhưng chức năng đó không tồn tại trong C # khi tôi viết mã này 9 năm trước.
Joren

28

Các Enumerable.SkipLast(IEnumerable<TSource>, Int32)phương pháp đã được bổ sung trong .NET chuẩn 2.1. Nó làm chính xác những gì bạn muốn.

IEnumerable<int> sequence = GetSequenceFromExpensiveSource();

var allExceptLast = sequence.SkipLast(1);

Từ https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast

Trả về một bộ sưu tập vô số mới có chứa các phần tử từ nguồn với các phần tử đếm cuối cùng của bộ sưu tập nguồn bị bỏ qua.


2
Điều này cũng tồn tại trong MoreLinq
Leperkawn 17/07/19

+1 cho SkipLast. Tôi không biết điều này vì gần đây tôi đến từ .NET Framework và họ không buồn thêm nó vào đó.
PRman

12

Không có gì trong BCL (hoặc MoreLinq tôi tin), nhưng bạn có thể tạo phương thức mở rộng của riêng mình.

public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source)
{
    using (var enumerator = source.GetEnumerator())
        bool first = true;
        T prev;
        while(enumerator.MoveNext())
        {
            if (!first)
                yield return prev;
            first = false;
            prev = enumerator.Current;
        }
    }
}

Mã này sẽ không hoạt động ... có lẽ nên if (!first)và rút first = falsera nếu.
Caleb

Mã này nhìn ra. Logic dường như là 'trả lại số chưa được khởi tạo prevtrong lần lặp đầu tiên và lặp lại mãi mãi sau đó'.
Phil Miller

Mã dường như có lỗi "biên dịch thời gian". Có thể là bạn muốn sửa nó. Nhưng vâng, chúng ta có thể viết một bộ mở rộng lặp lại một lần và trả về tất cả trừ mục cuối cùng.
Manish Basantani

@Caleb: Bạn hoàn toàn đúng - Tôi đã viết điều này một cách vội vàng! Đã sửa bây giờ. @Amby: Erm, không chắc bạn đang sử dụng trình biên dịch nào. Tôi không có gì của loại. : P
Noldorin

@RobertSchmidt Rất tiếc, vâng. Tôi đã thêm một usingtuyên bố bây giờ.
Noldorin

7

Sẽ rất hữu ích nếu .NET Framework được xuất xưởng với phương thức mở rộng như thế này.

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
    var enumerator = source.GetEnumerator();
    var queue = new Queue<T>(count + 1);

    while (true)
    {
        if (!enumerator.MoveNext())
            break;
        queue.Enqueue(enumerator.Current);
        if (queue.Count > count)
            yield return queue.Dequeue();
    }
}

3

Một sự mở rộng nhẹ về giải pháp tao nhã của Joren:

public static IEnumerable<T> Shrink<T>(this IEnumerable<T> source, int left, int right)
{
    int i = 0;
    var buffer = new Queue<T>(right + 1);

    foreach (T x in source)
    {
        if (i >= left) // Read past left many elements at the start
        {
            buffer.Enqueue(x);
            if (buffer.Count > right) // Build a buffer to drop right many elements at the end
                yield return buffer.Dequeue();    
        } 
        else i++;
    }
}
public static IEnumerable<T> WithoutLast<T>(this IEnumerable<T> source, int n = 1)
{
    return source.Shrink(0, n);
}
public static IEnumerable<T> WithoutFirst<T>(this IEnumerable<T> source, int n = 1)
{
    return source.Shrink(n, 0);
}

Trong đó thu nhỏ thực hiện một số đếm đơn giản về phía trước để thả leftnhiều phần tử đầu tiên và bộ đệm bị loại bỏ tương tự để loại bỏ rightnhiều phần tử cuối cùng .


3

nếu bạn không có thời gian để triển khai tiện ích mở rộng của riêng mình, đây là cách nhanh hơn:

var next = sequence.First();
sequence.Skip(1)
    .Select(s => 
    { 
        var selected = next;
        next = s;
        return selected;
    });

2

Một biến thể nhỏ trên câu trả lời được chấp nhận, mà (theo sở thích của tôi) thì đơn giản hơn một chút:

    public static IEnumerable<T> AllButLast<T>(this IEnumerable<T> enumerable, int n = 1)
    {
        // for efficiency, handle degenerate n == 0 case separately 
        if (n == 0)
        {
            foreach (var item in enumerable)
                yield return item;
            yield break;
        }

        var queue = new Queue<T>(n);
        foreach (var item in enumerable)
        {
            if (queue.Count == n)
                yield return queue.Dequeue();

            queue.Enqueue(item);
        }
    }

2

Nếu bạn có thể có được Counthoặc vô Lengthsố, trong hầu hết các trường hợp bạn có thể, thì chỉ cầnTake(n - 1)

Ví dụ với mảng

int[] arr = new int[] { 1, 2, 3, 4, 5 };
int[] sub = arr.Take(arr.Length - 1).ToArray();

Ví dụ với IEnumerable<T>

IEnumerable<int> enu = Enumerable.Range(1, 100);
IEnumerable<int> sub = enu.Take(enu.Count() - 1);

Câu hỏi là về IEnumerables và giải pháp của bạn là những gì OP đang cố gắng tránh. Mã của bạn có tác động hiệu suất.
nawfal

1

Tại sao không chỉ .ToList<type>()trên chuỗi, sau đó gọi số đếm và thực hiện như bạn đã làm ban đầu..nhưng vì nó đã được kéo vào một danh sách, nó không nên thực hiện một phép liệt kê đắt tiền hai lần. Đúng?


1

Giải pháp mà tôi sử dụng cho vấn đề này phức tạp hơn một chút.

Lớp tĩnh của tôi chứa một phương thức mở rộng MarkEndgiúp chuyển đổi các T-items trong EndMarkedItem<T>-items. Mỗi phần tử được đánh dấu bằng một phần phụ int, là 0 ; hoặc (trong trường hợp một người đặc biệt quan tâm đến 3 mục cuối) -3 , -2 hoặc -1 cho 3 mục cuối.

Điều này có thể hữu ích về riêng của mình, ví dụ như khi bạn muốn tạo ra một danh sách một cách đơn giản foreach-loop bằng dấu phẩy sau mỗi yếu tố ngoại trừ người cuối cùng 2, với thứ hai-to-cuối cùng mục tiếp theo là một từ kết hợp (chẳng hạn như “ ” hoặc hay hoặc ) và phần tử cuối cùng theo sau là một điểm.

Để tạo toàn bộ danh sách mà không có n mục cuối cùng , phương thức mở rộng ButLastchỉ đơn giản lặp lại trong EndMarkedItem<T>s EndMark == 0.

Nếu bạn không chỉ định tailLength, chỉ mục cuối cùng được đánh dấu (trong MarkEnd()) hoặc bỏ (trong ButLast()).

Giống như các giải pháp khác, điều này hoạt động bằng cách đệm.

using System;
using System.Collections.Generic;
using System.Linq;

namespace Adhemar.Util.Linq {

    public struct EndMarkedItem<T> {
        public T Item { get; private set; }
        public int EndMark { get; private set; }

        public EndMarkedItem(T item, int endMark) : this() {
            Item = item;
            EndMark = endMark;
        }
    }

    public static class TailEnumerables {

        public static IEnumerable<T> ButLast<T>(this IEnumerable<T> ts) {
            return ts.ButLast(1);
        }

        public static IEnumerable<T> ButLast<T>(this IEnumerable<T> ts, int tailLength) {
            return ts.MarkEnd(tailLength).TakeWhile(te => te.EndMark == 0).Select(te => te.Item);
        }

        public static IEnumerable<EndMarkedItem<T>> MarkEnd<T>(this IEnumerable<T> ts) {
            return ts.MarkEnd(1);
        }

        public static IEnumerable<EndMarkedItem<T>> MarkEnd<T>(this IEnumerable<T> ts, int tailLength) {
            if (tailLength < 0) {
                throw new ArgumentOutOfRangeException("tailLength");
            }
            else if (tailLength == 0) {
                foreach (var t in ts) {
                    yield return new EndMarkedItem<T>(t, 0);
                }
            }
            else {
                var buffer = new T[tailLength];
                var index = -buffer.Length;
                foreach (var t in ts) {
                    if (index < 0) {
                        buffer[buffer.Length + index] = t;
                        index++;
                    }
                    else {
                        yield return new EndMarkedItem<T>(buffer[index], 0);
                        buffer[index] = t;
                        index++;
                        if (index == buffer.Length) {
                            index = 0;
                        }
                    }
                }
                if (index >= 0) {
                    for (var i = index; i < buffer.Length; i++) {
                        yield return new EndMarkedItem<T>(buffer[i], i - buffer.Length - index);
                    }
                    for (var j = 0; j < index; j++) {
                        yield return new EndMarkedItem<T>(buffer[j], j - index);
                    }
                }
                else {
                    for (var k = 0; k < buffer.Length + index; k++) {
                        yield return new EndMarkedItem<T>(buffer[k], k - buffer.Length - index);
                    }
                }
            }    
        }
    }
}

1
    public static IEnumerable<T> NoLast<T> (this IEnumerable<T> items) {
        if (items != null) {
            var e = items.GetEnumerator();
            if (e.MoveNext ()) {
                T head = e.Current;
                while (e.MoveNext ()) {
                    yield return head; ;
                    head = e.Current;
                }
            }
        }
    }

1

Tôi không nghĩ rằng nó có thể trở nên cô đọng hơn thế này - cũng đảm bảo loại bỏ IEnumerator<T>:

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source)
{
    using (var it = source.GetEnumerator())
    {
        if (it.MoveNext())
        {
            var item = it.Current;
            while (it.MoveNext())
            {
                yield return item;
                item = it.Current;
            }
        }
    }
}

Chỉnh sửa: kỹ thuật giống hệt với câu trả lời này .


1

Với C # 8.0, bạn có thể sử dụng Phạm vi và chỉ số cho điều đó.

var allButLast = sequence[..^1];

Theo mặc định, C # 8.0 yêu cầu .NET Core 3.0 hoặc .NET Standard 2.1 (hoặc cao hơn). Kiểm tra chủ đề này để sử dụng với các triển khai cũ hơn.


0

Bạn có thể viết:

var list = xyz.Select(x=>x.Id).ToList();
list.RemoveAt(list.Count - 1);

0

Đây là một giải pháp thanh lịch chung và IMHO sẽ xử lý tất cả các trường hợp chính xác:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        IEnumerable<int> r = Enumerable.Range(1, 20);
        foreach (int i in r.AllButLast(3))
            Console.WriteLine(i);

        Console.ReadKey();
    }
}

public static class LinqExt
{
    public static IEnumerable<T> AllButLast<T>(this IEnumerable<T> enumerable, int n = 1)
    {
        using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
        {
            Queue<T> queue = new Queue<T>(n);

            for (int i = 0; i < n && enumerator.MoveNext(); i++)
                queue.Enqueue(enumerator.Current);

            while (enumerator.MoveNext())
            {
                queue.Enqueue(enumerator.Current);
                yield return queue.Dequeue();
            }
        }
    }
}

-1

IEnumerableCách tiếp cận truyền thống của tôi :

/// <summary>
/// Skips first element of an IEnumerable
/// </summary>
/// <typeparam name="U">Enumerable type</typeparam>
/// <param name="models">The enumerable</param>
/// <returns>IEnumerable of type skipping first element</returns>
private IEnumerable<U> SkipFirstEnumerable<U>(IEnumerable<U> models)
{
    using (var e = models.GetEnumerator())
    {
        if (!e.MoveNext()) return;
        for (;e.MoveNext();) yield return e.Current;
        yield return e.Current;
    }
}

/// <summary>
/// Skips last element of an IEnumerable
/// </summary>
/// <typeparam name="U">Enumerable type</typeparam>
/// <param name="models">The enumerable</param>
/// <returns>IEnumerable of type skipping last element</returns>
private IEnumerable<U> SkipLastEnumerable<U>(IEnumerable<U> models)
{
    using (var e = models.GetEnumerator())
    {
        if (!e.MoveNext()) return;
        yield return e.Current;
        for (;e.MoveNext();) yield return e.Current;
    }
}

SkipLastEnumerable của bạn có thể là truyền thống, nhưng bị hỏng. Phần tử đầu tiên mà nó trả về luôn là một U không xác định - ngay cả khi "mô hình" có 1 phần tử. Trong trường hợp sau, tôi mong đợi một kết quả trống.
Robert Schmidt

Ngoài ra, IEnumerator <T> là IDis Dùng một lần.
Robert Schmidt

Đúng / ghi chú. Cảm ơn bạn.
Chibueze Opata

-1

Một cách đơn giản là chỉ cần chuyển đổi thành hàng đợi và dequeue cho đến khi chỉ còn lại số lượng mục bạn muốn bỏ qua.

public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
    var queue = new Queue<T>(source);

    while (queue.Count() > n)
    {
        yield return queue.Dequeue();
    }
}

Take dùng để lấy số lượng vật phẩm đã biết. Và xếp hàng cho đủ lớn vô số là khủng khiếp.
Sinatr

-2

Có thể là:

var allBuLast = sequence.TakeWhile(e => e != sequence.Last());

Tôi đoán nó nên giống như "Ở đâu" nhưng giữ trật tự (?).


3
Đó là một cách rất không hiệu quả để làm điều đó. Để đánh giá Sequ.Last () nó phải duyệt toàn bộ chuỗi, làm như vậy cho từng phần tử trong chuỗi. Hiệu suất O (n ^ 2).
Mike

Bạn đúng rồi. Tôi không biết tôi đã nghĩ gì khi viết XD này. Dù sao, bạn có chắc chắn rằng Last () sẽ đi qua toàn bộ chuỗi không? Đối với một số triển khai IEnumerable (như Array), đây phải là O (1). Tôi đã không kiểm tra việc thực hiện Danh sách, nhưng nó cũng có thể có một trình lặp "đảo ngược", bắt đầu từ phần tử cuối cùng, cũng sẽ mất O (1). Ngoài ra, bạn nên tính đến việc O (n) = O (2n), ít nhất là về mặt kỹ thuật. Do đó, nếu quy trình này không hoàn toàn chỉ trích hiệu suất ứng dụng của bạn, tôi sẽ gắn bó với trình tự rõ ràng hơn nhiều. Thực hiện (Sequ.Count () - 1) .Regards!
Guillermo Ares

@Mike Tôi không đồng ý với bạn đời của bạn, Sequ.Last () là O (1) để không cần phải duyệt toàn bộ chuỗi. stackoverflow.com/a/1377895/812598
GoRoS

1
@GoRoS, nó chỉ là O (1) nếu chuỗi thực hiện ICollection / IList hoặc là một mảng. Tất cả các chuỗi khác là O (N). Trong câu hỏi của tôi, tôi không cho rằng một trong những nguồn O (1)
Mike

3
Chuỗi có thể có một số mục thỏa mãn điều kiện này e == Sequ.Last (), ví dụ mới [] {1, 1, 1, 1}
Sergey Shandar

-2

Nếu tốc độ là một yêu cầu, cách học cũ này sẽ là nhanh nhất, mặc dù mã không có vẻ trơn tru như linq có thể làm được.

int[] newSequence = int[sequence.Length - 1];
for (int x = 0; x < sequence.Length - 1; x++)
{
    newSequence[x] = sequence[x];
}

Điều này đòi hỏi chuỗi đó là một mảng vì nó có độ dài cố định và các mục được lập chỉ mục.


2
Bạn đang làm việc với một IEnumerable không cho phép truy cập vào các phần tử thông qua một chỉ mục. Giải pháp của bạn không hoạt động. Giả sử bạn làm đúng, nó yêu cầu duyệt qua chuỗi để xác định độ dài, phân bổ một mảng có độ dài n-1, sao chép tất cả các phần tử. - 1. Hoạt động 2n-1 và (2n-1) * (4 hoặc 8 byte) bộ nhớ. Điều đó thậm chí không nhanh.
Tarik

-4

Tôi có thể sẽ làm một cái gì đó như thế này:

sequence.Where(x => x != sequence.LastOrDefault())

Đây là một lần lặp với một kiểm tra rằng nó không phải là lần cuối cùng cho mỗi lần.


3
Hai lý do đó không phải là một điều tốt để làm. 1) .LastOrDefault () yêu cầu lặp lại toàn bộ chuỗi và được gọi cho từng phần tử trong chuỗi (trong .Where ()). 2) Nếu chuỗi là [1,2,1,2,1,2] và bạn đã sử dụng kỹ thuật của mình, bạn sẽ bị bỏ lại với [1,1,1].
Mike
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.