จะนำองค์ประกอบทั้งหมดยกเว้นองค์ประกอบสุดท้ายในลำดับโดยใช้ LINQ ได้อย่างไร


131

สมมติว่าฉันมีลำดับ

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

การหาลำดับไม่ถูกและถูกสร้างขึ้นแบบไดนามิกและฉันต้องการทำซ้ำครั้งเดียวเท่านั้น

ฉันต้องการได้ 0 - 999999 (เช่นทุกอย่างยกเว้นองค์ประกอบสุดท้าย)

ฉันรู้ว่าฉันสามารถทำสิ่งต่างๆเช่น:

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

แต่ส่งผลให้เกิดการแจงนับสองครั้งในลำดับใหญ่

มีโครงสร้าง LINQ ที่ให้ฉันทำ:

sequence.TakeAllButTheLastElement();

3
คุณต้องเลือกระหว่างอัลกอริธึมประสิทธิภาพพื้นที่ O (2n) หรือ O (count) โดยที่อย่างหลังต้องย้ายรายการในอาร์เรย์ภายในด้วย
Dykam

1
ดาริโอคุณช่วยอธิบายสำหรับคนที่ไม่ได้เป็นตัวใหญ่ได้ไหม?
เล็กซ์

ดูคำถามที่ซ้ำกันนี้: stackoverflow.com/q/4166493/240733
stakx - ไม่ร่วมให้ข้อมูลใน

sequenceList.RemoveAt(sequence.Count - 1);ฉันสิ้นสุดกับแคชมันโดยการแปลงคอลเลกชันในรายการแล้วโทร ในกรณีของฉันเป็นที่ยอมรับได้เพราะหลังจากการปรับแต่ง LINQ ทั้งหมดฉันต้องแปลงเป็นอาร์เรย์หรือIReadOnlyCollectionอย่างไรก็ตาม ฉันสงสัยว่ากรณีการใช้งานของคุณคืออะไรที่คุณไม่ได้พิจารณาการแคช? อย่างที่ฉันเห็นแม้แต่คำตอบที่ได้รับการอนุมัติการแคชบางอย่างดังนั้นการแปลงListเป็นแบบธรรมดาจึงง่ายกว่าและสั้นกว่ามากในความคิดของฉัน
Pavels Ahmadulins

คำตอบ:


64

ฉันไม่รู้จักโซลูชัน Linq - แต่คุณสามารถโค้ดอัลกอริทึมได้อย่างง่ายดายด้วยตัวเองโดยใช้เครื่องกำเนิดไฟฟ้า (ผลตอบแทน)

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()));
}

หรือเป็นโซลูชันทั่วไปที่ทิ้ง n รายการสุดท้าย (โดยใช้คิวตามที่แนะนำในความคิดเห็น):

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
ตอนนี้คุณสามารถสรุปให้ใช้ทั้งหมดยกเว้น n สุดท้ายได้หรือไม่?
Eric Lippert

4
ดี ข้อผิดพลาดเล็ก ๆ น้อย ๆ ขนาดคิวควรเริ่มต้นเป็น n + 1 เนื่องจากเป็นขนาดสูงสุดของคิว
Eric Lippert

ReSharper ไม่เข้าใจรหัสของคุณ (การกำหนดในนิพจน์เงื่อนไข ) แต่ฉันชอบมัน +1
Грозный

44

เพื่อเป็นทางเลือกในการสร้างวิธีการของคุณเองและในกรณีที่ลำดับองค์ประกอบไม่สำคัญขั้นตอนต่อไปจะใช้งานได้:

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

49
โปรดทราบว่าสิ่งนี้ต้องการให้คุณมีหน่วยความจำเพียงพอที่จะจัดเก็บลำดับทั้งหมดและแน่นอนว่ามันจะยังคงวนซ้ำลำดับทั้งหมดสองครั้งครั้งเดียวเพื่อสร้างลำดับย้อนกลับและอีกครั้งเพื่อทำซ้ำ ค่อนข้างแย่กว่าโซลูชัน Count ไม่ว่าคุณจะหั่นมันอย่างไร ช้ากว่าและใช้หน่วยความจำมากขึ้น
Eric Lippert

ฉันไม่รู้ว่าวิธีการ 'ย้อนกลับ' ทำงานอย่างไรดังนั้นฉันจึงไม่แน่ใจเกี่ยวกับจำนวนหน่วยความจำที่ใช้ แต่ฉันเห็นด้วยเกี่ยวกับการทำซ้ำสองครั้ง ไม่ควรใช้วิธีนี้กับคอลเลกชันขนาดใหญ่หรือหากประสิทธิภาพมีความสำคัญ
Kamarey

5
คุณจะใช้ Reverse ได้อย่างไร? คุณสามารถหาวิธีโดยทั่วไปโดยไม่ต้องจัดเก็บลำดับทั้งหมดได้หรือไม่?
Eric Lippert

2
ฉันชอบมันและเป็นไปตามเกณฑ์ของการไม่สร้างลำดับสองครั้ง
Amy B

12
และนอกจากนี้คุณจะต้องย้อนกลับลำดับทั้งหมดอีกครั้งเพื่อให้มันequence.Reverse().Skip(1).Reverse()ไม่ใช่ทางออกที่ดี
shashwat

42

เนื่องจากฉันไม่ใช่แฟนของการใช้ an อย่างชัดเจนEnumeratorนี่เป็นทางเลือกอื่น โปรดทราบว่าต้องใช้วิธีการ wrapper เพื่อให้อาร์กิวเมนต์ที่ไม่ถูกต้องโยนก่อนเวลาแทนที่จะเลื่อนการตรวจสอบออกไปจนกว่าจะมีการแจกแจงลำดับตามความเป็นจริง

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;
    }
}

ตามคำแนะนำของ Eric Lippert มันสามารถสรุปได้อย่างง่ายดายกับ n รายการ:

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();
    }
}

ตอนนี้ฉันบัฟเฟอร์ก่อนที่จะให้ผลแทนที่จะเป็นหลังจากให้ผลเพื่อให้n == 0กรณีไม่จำเป็นต้องมีการจัดการพิเศษ


ในตัวอย่างแรกมันอาจจะเป็นตลอดจึงเร็วขึ้นเล็กน้อยไปยังชุดในข้ออื่นก่อนกำหนดbuffered=false bufferเงื่อนไขนี้กำลังได้รับการตรวจสอบอยู่แล้ว แต่จะหลีกเลี่ยงการตั้งค่าซ้ำซ้อนbufferedทุกครั้งที่ผ่านลูป
James

ใครช่วยบอกข้อดี / ข้อเสียของข้อนี้กับคำตอบที่เลือกได้ไหม
สินจั

ข้อดีของการใช้งานในวิธีแยกต่างหากที่ขาดการตรวจสอบอินพุตคืออะไร? นอกจากนี้ฉันเพียงแค่ทิ้งการใช้งานเดียวและให้ค่าเริ่มต้นสำหรับการใช้งานหลายรายการ
jpmc26

@ jpmc26 DropLastกับการตรวจสอบในวิธีการที่แยกต่างหากคุณจริงได้รับการตรวจสอบขณะที่คุณเรียก มิฉะนั้นการตรวจสอบความถูกต้องจะเกิดขึ้นก็ต่อเมื่อคุณระบุลำดับจริง ๆ (ในการเรียกครั้งแรกถึงMoveNextผลลัพธ์IEnumerator) ไม่ใช่เรื่องสนุกที่จะแก้จุดบกพร่องเมื่ออาจมีรหัสจำนวนหนึ่งระหว่างการสร้างIEnumerableและการแจกแจงตามความเป็นจริง ทุกวันนี้ฉันเขียนInternalDropLastเป็นฟังก์ชันภายในDropLastแต่ฟังก์ชันนั้นไม่มีอยู่ใน C # เมื่อฉันเขียนโค้ดนี้เมื่อ 9 ปีที่แล้ว
Joren

29

Enumerable.SkipLast(IEnumerable<TSource>, Int32)วิธีการที่ถูกเพิ่มเข้ามาใน .NET มาตรฐาน 2.1 มันทำในสิ่งที่คุณต้องการ

IEnumerable<int> sequence = GetSequenceFromExpensiveSource();

var allExceptLast = sequence.SkipLast(1);

จากhttps://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast

ส่งคืนคอลเล็กชันที่แจกแจงได้ใหม่ที่มีองค์ประกอบจากแหล่งที่มาโดยไม่ใส่องค์ประกอบการนับสุดท้ายของคอลเล็กชันต้นทาง


2
สิ่งนี้มีอยู่ใน MoreLinq
Leperkawn เมื่อ

+1 สำหรับ SkipLast ฉันไม่รู้เรื่องนี้ตั้งแต่ฉันเพิ่งมาจาก. NET Framework และพวกเขาไม่ต้องกังวลที่จะเพิ่มเข้าไปที่นั่น
PRMan

12

ไม่มีอะไรใน BCL (หรือฉันเชื่อว่า MoreLinq) แต่คุณสามารถสร้างวิธีการขยายของคุณเองได้

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;
        }
    }
}

รหัสนี้ใช้ไม่ได้ ... ควรจะเป็นif (!first)และดึงfirst = falseออกจาก if
Caleb

รหัสนี้ปิดอยู่ ตรรกะดูเหมือนจะเป็น 'ส่งคืนค่าที่ไม่ได้เริ่มต้นprevในการทำซ้ำครั้งแรกและวนซ้ำตลอดไปหลังจากนั้น'
Phil Miller

ดูเหมือนว่าโค้ดจะมีข้อผิดพลาด "เวลารวบรวม" คุณอาจต้องการแก้ไข แต่ใช่เราสามารถเขียน Extender ซึ่งวนซ้ำเพียงครั้งเดียวและส่งคืนรายการทั้งหมดยกเว้นรายการสุดท้าย
Manish Basantani

@Caleb: คุณพูดถูก - ฉันเขียนสิ่งนี้ด้วยความเร่งรีบจริงๆ! แก้ไขแล้ว @Amby: เอิ่มไม่แน่ใจว่าคุณใช้คอมไพเลอร์อะไร ฉันไม่มีอะไรในการจัดเรียง : P
Noldorin

@RobertSchmidt อ๊ะใช่แล้ว ฉันเพิ่มusingคำสั่งตอนนี้
Noldorin

7

จะเป็นประโยชน์หากมีการจัดส่ง. NET Framework ด้วยวิธีการขยายเช่นนี้

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

การขยายตัวเล็กน้อยในโซลูชันที่หรูหราของ 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);
}

โดยที่การย่อขนาดจะใช้การนับไปข้างหน้าอย่างง่ายเพื่อดร็อปleftองค์ประกอบจำนวนมากแรกและบัฟเฟอร์ที่ทิ้งเดียวกันเพื่อทิ้งrightองค์ประกอบสุดท้าย


3

หากคุณไม่มีเวลาเปิดตัวส่วนขยายของคุณเองนี่เป็นวิธีที่เร็วกว่า:

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

2

การเปลี่ยนแปลงเล็กน้อยในคำตอบที่ยอมรับซึ่ง (สำหรับรสนิยมของฉัน) นั้นง่ายกว่าเล็กน้อย:

    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

หากคุณสามารถแจกแจงCountหรือLengthแจกแจงได้ซึ่งโดยส่วนใหญ่แล้วคุณสามารถทำได้Take(n - 1)

ตัวอย่างที่มีอาร์เรย์

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

ตัวอย่างด้วย IEnumerable<T>

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

คำถามเกี่ยวกับ IEnumerables และวิธีแก้ปัญหาของคุณคือสิ่งที่ OP พยายามหลีกเลี่ยง รหัสของคุณมีผลต่อประสิทธิภาพ
nawfal

1

ทำไมไม่แค่.ToList<type>()เรียงตามลำดับแล้วนับการโทรและใช้เวลาเหมือนที่คุณทำในตอนแรก .. แต่เนื่องจากมันถูกดึงเข้าไปในรายการจึงไม่ควรทำการแจงนับราคาแพงสองครั้ง ขวา?


1

วิธีแก้ปัญหาที่ฉันใช้สำหรับปัญหานี้ซับซ้อนกว่าเล็กน้อย

คลาส util คงที่ของฉันมีเมธอดส่วนขยายMarkEndที่แปลงT-items ในEndMarkedItem<T>-items แต่ละองค์ประกอบมีเครื่องหมายพิเศษintซึ่งก็คือ0 ; หรือ (ในกรณีที่สนใจเป็นพิเศษใน 3 รายการสุดท้าย) -3 , -2หรือ-1สำหรับ 3 รายการสุดท้าย

สิ่งนี้อาจมีประโยชน์ในตัวมันเองเช่นเมื่อคุณต้องการสร้างรายการในforeach-loop แบบง่ายที่มีเครื่องหมายจุลภาคหลังแต่ละองค์ประกอบยกเว้น 2 รายการสุดท้ายโดยมีรายการที่สองถึงสุดท้ายตามด้วยคำร่วม (เช่น“ และ ” หรือ“ หรือ ”) และองค์ประกอบสุดท้ายตามด้วยจุด

สำหรับการสร้างรายการทั้งหมดโดยไม่มีnรายการสุดท้ายวิธีการขยายButLastเพียงแค่วนซ้ำในEndMarkedItem<T>ขณะที่EndMark == 0ในขณะที่

หากคุณไม่ระบุtailLengthเฉพาะรายการสุดท้ายเท่านั้นที่ถูกทำเครื่องหมาย (ในMarkEnd()) หรือตกหล่น (ในButLast())

เช่นเดียวกับโซลูชันอื่น ๆ สิ่งนี้ทำงานโดยการบัฟเฟอร์

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

ฉันไม่คิดว่ามันจะรวบรัดไปกว่านี้ - ยังมั่นใจว่าจะกำจัด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;
            }
        }
    }
}

แก้ไข: เทคนิคเหมือนกันกับคำตอบนี้


1

ด้วย C # 8.0 คุณสามารถใช้ช่วงและดัชนีสำหรับสิ่งนั้นได้

var allButLast = sequence[..^1];

โดยค่าเริ่มต้น C # 8.0 ต้องการ. NET Core 3.0 หรือ. NET Standard 2.1 (หรือสูงกว่า) ตรวจสอบเธรดนี้เพื่อใช้กับการใช้งานรุ่นเก่า



0

นี่เป็นวิธีแก้ปัญหาทั่วไปและ IMHO ที่สวยงามซึ่งจะจัดการทุกกรณีอย่างถูกต้อง:

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

IEnumerableแนวทางดั้งเดิมของฉัน:

/// <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 ของคุณอาจเป็นแบบดั้งเดิม แต่เสีย องค์ประกอบแรกที่ส่งคืนจะเป็น U ที่ไม่ได้กำหนดไว้เสมอแม้ว่า "model" จะมี 1 องค์ประกอบก็ตาม ในกรณีหลังฉันคาดหวังว่าจะได้ผลลัพธ์ที่ว่างเปล่า
Robert Schmidt

นอกจากนี้ IEnumerator <T> เป็น IDisposable
Robert Schmidt

ทรู / ตั้งข้อสังเกต ขอบคุณ.
Chibueze Opata

-1

วิธีง่ายๆก็แค่แปลงเป็นคิวและ dequeue จนกว่าจะเหลือเพียงจำนวนรายการที่คุณต้องการข้าม

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ใช้เพื่อรับทราบจำนวนรายการ และคิวสำหรับการแจกแจงที่ใหญ่พอนั้นแย่มาก
Sinatr

-2

อาจจะเป็น:

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

ฉันเดาว่ามันน่าจะเหมือนเดอ "ที่ไหน" แต่รักษาคำสั่ง (?)


3
นั่นเป็นวิธีที่ไม่มีประสิทธิภาพมากที่จะทำ ในการประเมินลำดับ Last () จะต้องสำรวจลำดับทั้งหมดโดยทำเช่นนั้นสำหรับแต่ละองค์ประกอบในลำดับ ประสิทธิภาพ O (n ^ 2)
Mike

คุณพูดถูก ฉันไม่รู้ว่าฉันคิดอะไรอยู่ตอนที่เขียน XD นี้ อย่างไรก็ตามคุณแน่ใจหรือไม่ว่า Last () จะข้ามลำดับทั้งหมด? สำหรับการใช้งาน IEnumerable บางอย่าง (เช่น Array) ควรเป็น O (1) ฉันไม่ได้ตรวจสอบการใช้งานรายการ แต่อาจมีตัววนซ้ำ "ย้อนกลับ" โดยเริ่มจากองค์ประกอบสุดท้ายซึ่งจะใช้ O (1) ด้วย นอกจากนี้คุณควรคำนึงว่า O (n) = O (2n) อย่างน้อยก็พูดในทางเทคนิค ดังนั้นหากขั้นตอนนี้ไม่ได้วิจารณ์ประสิทธิภาพแอปของคุณอย่างแน่นอนฉันจะยึดตามลำดับที่ชัดเจนกว่ามากรับ (ลำดับนับ () - 1) ขอแสดงความนับถือ!
Guillermo Ares

@ ไมค์ฉันไม่เห็นด้วยกับคุณคู่ลำดับสุดท้าย () คือ O (1) เพื่อที่จะได้ไม่ต้องสำรวจลำดับทั้งหมด stackoverflow.com/a/1377895/812598
GoRoS

1
@GoRoS เป็นเพียง O (1) ถ้าลำดับใช้ ICollection / IList หรือเป็น Array ลำดับอื่น ๆ ทั้งหมดคือ O (N) ในคำถามของฉันฉันไม่คิดว่าแหล่งที่มา O (1) แห่งหนึ่ง
Mike

3
ลำดับอาจมีหลายรายการที่ตรงตามเงื่อนไขนี้ e == ลำดับสุดท้าย () เช่น new [] {1, 1, 1, 1}
Sergey Shandar

-2

หากต้องการความเร็ววิธีนี้ของโรงเรียนเก่าควรเร็วที่สุดแม้ว่ารหัสจะดูไม่ราบรื่นเท่าที่ linq สามารถทำได้

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

สิ่งนี้ต้องการให้ลำดับเป็นอาร์เรย์เนื่องจากมีความยาวคงที่และรายการที่จัดทำดัชนี


2
คุณกำลังจัดการกับ IEnumerable ที่ไม่อนุญาตให้เข้าถึงองค์ประกอบผ่านดัชนี โซลูชันของคุณไม่ทำงาน สมมติว่าคุณทำถูกต้องจำเป็นต้องมีการข้ามลำดับเพื่อกำหนดความยาวจัดสรรอาร์เรย์ของความยาว n-1 คัดลอกองค์ประกอบทั้งหมด - 1. การดำเนินการ 2n-1 และ (2n-1) * (4 หรือ 8 ไบต์) ของหน่วยความจำ มันไม่เร็วเลย
Tarik

-4

ฉันอาจจะทำสิ่งนี้:

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

นี่เป็นการทำซ้ำครั้งเดียวโดยตรวจสอบว่าไม่ใช่ครั้งสุดท้ายในแต่ละครั้ง


3
เหตุผลสองประการที่ไม่ควรทำ 1) .LastOrDefault () ต้องการการวนซ้ำทั้งลำดับและเรียกใช้สำหรับแต่ละองค์ประกอบในลำดับ (ใน. Where ()) 2) ถ้าลำดับคือ [1,2,1,2,1,2] และคุณใช้เทคนิคของคุณคุณจะเหลือ [1,1,1]
Mike
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.