ฉันจะรับทุกรายการที่ n จากรายการ <T> ได้อย่างไร?


115

ฉันใช้. NET 3.5 และต้องการได้รับทุก ๆ * n* th จากรายการ ฉันไม่ได้ใส่ใจว่ามันทำได้โดยใช้นิพจน์แลมบ์ดาหรือ LINQ

แก้ไข

ดูเหมือนคำถามนี้จะกระตุ้นให้เกิดการถกเถียงกันมากทีเดียว (ซึ่งเป็นสิ่งที่ดีใช่มั้ย?) สิ่งสำคัญที่ฉันได้เรียนรู้คือเมื่อคุณคิดว่าคุณรู้ทุกวิธีในการทำบางสิ่งบางอย่าง (แม้จะง่ายอย่างนี้ก็ตาม) ให้คิดใหม่!


ฉันไม่ได้แก้ไขความหมายใด ๆ ที่อยู่เบื้องหลังคำถามเดิมของคุณ ฉันทำความสะอาดและใช้อักษรตัวพิมพ์ใหญ่และเครื่องหมายวรรคตอนอย่างถูกต้องเท่านั้น (.NET เป็นตัวพิมพ์ใหญ่ LINQ เป็นตัวพิมพ์ใหญ่ทั้งหมดและไม่ใช่ 'lambda' แต่เป็น 'lambda expression')
George Stocker

1
คุณแทนที่ "เอะอะ" ด้วย "แน่ใจ" ซึ่งไม่ใช่คำพ้องความหมายทั้งหมด
mqp

ดูเหมือนจะเป็นเช่นนั้น การแน่ใจก็ไม่สมเหตุสมผลเช่นกันเว้นแต่ว่า "ฉันไม่แน่ใจว่าทำได้หรือไม่โดยใช้ ... "
ซามูเอล

ใช่อย่างที่ฉันเข้าใจถูกต้องแล้ว
mqp

เอะอะน่าจะดีที่สุดที่จะแทนที่ด้วย "ความกังวล" เพื่อให้อ่านว่า "ฉันไม่กังวลว่าจะทำได้โดยใช้นิพจน์แลมบ์ดาหรือ LINQ"
TheTXI

คำตอบ:


191
return list.Where((x, i) => i % nStep == 0);

5
@mquander: โปรดทราบว่าสิ่งนี้จะให้องค์ประกอบที่ n - 1 หากคุณต้องการองค์ประกอบที่ n จริง (ข้ามองค์ประกอบแรก) คุณจะต้องเพิ่ม 1 ใน i
casperOne

2
ใช่ฉันคิดว่ามันขึ้นอยู่กับสิ่งที่คุณหมายถึง "n" แต่การตีความของคุณอาจจะธรรมดากว่า เพิ่มหรือลบออกจาก i เพื่อให้เหมาะกับความต้องการของคุณ
mqp

5
หมายเหตุ: โซลูชัน Linq / Lambda จะมีประสิทธิภาพน้อยกว่าลูปแบบธรรมดาที่เพิ่มขึ้นคงที่
MartinStettner

5
ไม่จำเป็นด้วยการดำเนินการที่รอการตัดบัญชีสามารถใช้ใน foreach loop และวนซ้ำรายการเดิมได้เพียงครั้งเดียว
ซามูเอล

1
ขึ้นอยู่กับความหมายของคำว่า "ใช้ได้จริง" หากคุณต้องการวิธีที่รวดเร็วในการรับรายการอื่น ๆ ในรายการ 30 รายการเมื่อผู้ใช้คลิกปุ่มฉันจะบอกว่านี่เป็นประโยชน์ทุกอย่าง บางครั้งประสิทธิภาพก็ไม่สำคัญอีกต่อไป แน่นอนบางครั้งก็ไม่
mqp

37

ฉันรู้ว่ามันเป็น "โรงเรียนเก่า" แต่ทำไมไม่ใช้ for loop กับการก้าว = n


นั่นคือความคิดของฉัน
Mark Pim

1
@ Michael Todd: มันใช้งานได้ แต่ปัญหาคือคุณต้องทำซ้ำฟังก์ชันนั้นทุกที่ เมื่อใช้ LINQ จะกลายเป็นส่วนหนึ่งของแบบสอบถามที่ประกอบด้วย
casperOne

8
@casperOne: ฉันเชื่อว่าโปรแกรมเมอร์คิดค้นสิ่งนี้ที่เรียกว่ารูทีนย่อยเพื่อจัดการกับสิ่งนี้) ในโปรแกรมจริงฉันอาจใช้ลูปแม้ว่าจะเป็นเวอร์ชัน LINQ ที่ฉลาดก็ตามเนื่องจากการวนซ้ำหมายความว่าคุณไม่ต้องวนซ้ำทุกองค์ประกอบ ( เพิ่มดัชนีโดย N. )
mqp

ฉันเห็นด้วยกับวิธีแก้ปัญหาของโรงเรียนเก่าและฉันก็เดาได้ว่าสิ่งนี้จะทำได้ดีกว่า
Jesper Fyhr Knudsen

ง่ายต่อการดำเนินการด้วยไวยากรณ์ใหม่ที่สวยงาม มันสนุกดี
Ronnie

34

เสียงเหมือน

IEnumerator<T> GetNth<T>(List<T> list, int n) {
  for (int i=0; i<list.Count; i+=n)
    yield return list[i]
}

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

แก้ไข:

ทำมัน

public static class MyListExtensions {
  public static IEnumerable<T> GetNth<T>(this List<T> list, int n) {
    for (int i=0; i<list.Count; i+=n)
      yield return list[i];
  }
}

และคุณเขียนด้วยวิธี LINQish

from var element in MyList.GetNth(10) select element;

แก้ไขครั้งที่ 2 :

เพื่อให้ LINQish มากยิ่งขึ้น

from var i in Range(0, ((myList.Length-1)/n)+1) select list[n*i];

2
ฉันชอบวิธีนี้ในการใช้เมธอด [] นี้แทน Where () ซึ่งโดยพื้นฐานแล้วจะวนซ้ำทุกองค์ประกอบของ IE ที่ไม่สามารถคำนวณได้ หากคุณมีประเภท IList / ICollection นี่เป็นแนวทางที่ดีกว่า IMHO
spoulson

ไม่แน่ใจว่าวิธีการทำงานของรายการ แต่ทำไมคุณใช้ห่วงและกลับlist[i]แทนเพียงแค่กลับมาlist[n-1]?
Juan Carlos Oropeza

@JuanCarlosOropeza เขาส่งคืนทุกองค์ประกอบที่ n (เช่น 0, 3, 6 ... ) ไม่ใช่แค่องค์ประกอบที่ n ของรายการ
alfoks

27

คุณสามารถใช้ Where overload ซึ่งส่งผ่านดัชนีพร้อมกับองค์ประกอบ

var everyFourth = list.Where((x,i) => i % 4 == 0);

1
ต้องบอกว่าฉันเป็นแฟนของวิธีนี้
Quintin Robinson

1
ฉันลืมไปตลอดว่าคุณทำได้ - ดีมาก
Stephen Newman

10

สำหรับ Loop

for(int i = 0; i < list.Count; i += n)
    //Nth Item..

การนับจะประเมินการนับ หากสิ่งนี้ทำในลักษณะที่เป็นมิตรกับ linq คุณสามารถประเมินและรับค่า 100 แรกได้อย่างเกียจคร้านเช่น `` source.TakeEvery (5). Take (100) `` หากแหล่งที่มานั้นมีราคาแพงในการประเมินแนวทางของคุณจะทำให้ทุกๆ องค์ประกอบที่จะประเมิน
RhysC

1
@RhysC จุดดีสำหรับการแจงนับโดยทั่วไป OTOH มีการระบุคำถามList<T>ดังนั้นจึงCountกำหนดให้มีราคาถูก
ToolmakerSteve

3

ฉันไม่แน่ใจว่าจะใช้นิพจน์ LINQ ได้หรือไม่ แต่ฉันรู้ว่าคุณสามารถใช้Whereวิธีการขยายได้ ตัวอย่างเช่นเพื่อรับทุกรายการที่ห้า:

List<T> list = originalList.Where((t,i) => (i % 5) == 0).ToList();

สิ่งนี้จะได้รับรายการแรกและทุก ๆ ห้าจากที่นั่น หากคุณต้องการเริ่มต้นที่รายการที่ห้าแทนที่จะเป็นรายการแรกให้เปรียบเทียบกับ 4 แทนที่จะเปรียบเทียบด้วย 0


3

ฉันคิดว่าถ้าคุณให้ส่วนขยาย linq คุณควรจะสามารถใช้งานบนอินเทอร์เฟซที่เฉพาะเจาะจงน้อยที่สุดได้ดังนั้นบน IEnumerable แน่นอนว่าหากคุณพร้อมสำหรับความเร็วโดยเฉพาะสำหรับ N ขนาดใหญ่คุณอาจให้การเข้าถึงที่จัดทำดัชนีมากเกินไป อย่างหลังขจัดความจำเป็นในการทำซ้ำข้อมูลจำนวนมากที่ไม่จำเป็นและจะเร็วกว่าประโยค Where มาก การให้โอเวอร์โหลดทั้งสองแบบช่วยให้คอมไพลเลอร์เลือกตัวแปรที่เหมาะสมที่สุด

public static class LinqExtensions
{
    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n)
    {
        if (n < 0)
            throw new ArgumentOutOfRangeException("n");
        if (n > 0)
        {
            int c = 0;
            foreach (var e in list)
            {
                if (c % n == 0)
                    yield return e;
                c++;
            }
        }
    }
    public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
    {
        if (n < 0)
            throw new ArgumentOutOfRangeException("n");
        if (n > 0)
            for (int c = 0; c < list.Count; c += n)
                yield return list[c];
    }
}

สิ่งนี้ใช้ได้กับรายการใด ๆ ? เนื่องจากฉันพยายามใช้ใน List สำหรับคลาสที่กำหนดเองและส่งคืน IEnumarted <class> แทน <class> และการบังคับให้ Coversion (class) List.GetNth (1) ไม่ทำงาน
Juan Carlos Oropeza

เป็นความผิดของฉันฉันต้องรวม GetNth (1) .FirstOrDefault ();
Juan Carlos Oropeza

0
private static readonly string[] sequence = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15".Split(',');

static void Main(string[] args)
{
    var every4thElement = sequence
      .Where((p, index) => index % 4 == 0);

    foreach (string p in every4thElement)
    {
        Console.WriteLine("{0}", p);
    }

    Console.ReadKey();
}

เอาท์พุต

ใส่คำอธิบายภาพที่นี่


0

Imho ไม่มีคำตอบที่ถูกต้อง วิธีแก้ปัญหาทั้งหมดเริ่มต้นจาก 0 แต่ผมอยากได้องค์ประกอบที่ n

public static IEnumerable<T> GetNth<T>(this IList<T> list, int n)
{
    for (int i = n - 1; i < list.Count; i += n)
        yield return list[i];
}

0

@belucha ฉันชอบสิ่งนี้เพราะรหัสไคลเอนต์สามารถอ่านได้มากและ Compiler เลือก Implementation ที่มีประสิทธิภาพสูงสุด ฉันจะสร้างสิ่งนี้ขึ้นมาโดยการลดข้อกำหนดIReadOnlyList<T>และเพื่อบันทึก Division สำหรับ LINQ ประสิทธิภาพสูง:

    public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n) {
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        int i = n;
        foreach (var e in list) {
            if (++i < n) { //save Division
                continue;
            }
            i = 0;
            yield return e;
        }
    }

    public static IEnumerable<T> GetNth<T>(this IReadOnlyList<T> list, int n
        , int offset = 0) { //use IReadOnlyList<T>
        if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n), n, null);
        for (var i = offset; i < list.Count; i += n) {
            yield return list[i];
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.