Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

การใช้ Take และ Skip ร่วมกันใน LINQ C#

Take และ Skip Operators ใน LINQ

Take(n) - เอา n ตัวแรกจาก Sequence
Skip(n) - ข้าม n ตัวแรก แล้วเอาส่วนที่เหลือ

รวมกันใช้ได้: Paging (การแบ่งหน้า)

Syntax


// Skip แล้ว Take (สำคัญ: ลำดับนี้!)
var page = source.Skip(pageSize * (pageNumber - 1)).Take(pageSize);

// หรือ Query Syntax
var page = (from item in source
            select item)
           .Skip(pageSize * (pageNumber - 1))
           .Take(pageSize);

ตัวอย่าง: Paging Implementation

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

class Program
{
    static void Main()
    {
        // ข้อมูลตัวอย่าง 28 รายการ
        List numbers = new List 
        { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 5, 6, 7, 7, 8, 8 };
        
        Console.WriteLine($"Total: {numbers.Count}");  // 28
        
        int pageSize = 5;
        int totalPages = (int)Math.Ceiling((double)numbers.Count / pageSize);
        
        for (int page = 1; page <= totalPages; page++)
        {
            var pageData = numbers
                .Skip((page - 1) * pageSize)
                .Take(pageSize)
                .ToList();
            
            Console.WriteLine($"\nPage {page} ({pageData.Count} items): {string.Join(", ", pageData)}");
        }
    }
}

ผลลัพธ์:

Total: 28

Page 1 (5 items): 1, 2, 3, 4, 5
Page 2 (5 items): 6, 7, 8, 9, 11
Page 3 (5 items): 1, 2, 3, 4, 5
Page 4 (5 items): 6, 7, 8, 9, 1
Page 5 (5 items): 2, 3, 5, 6, 7
Page 6 (3 items): 7, 8, 8

ตัวอย่างที่ 2: Skip และ Take แยกกัน

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

class Program
{
    static void Main()
    {
        List numbers = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        
        // Skip 3 ตัวแรก
        var skip3 = numbers.Skip(3);
        Console.WriteLine($"Skip(3): {string.Join(", ", skip3)}");  // 4,5,6,7,8,9,10
        Console.WriteLine($"Count: {skip3.Count()}");  // 7
        
        // Take 4 ตัวแรก
        var take4 = numbers.Take(4);
        Console.WriteLine($"Take(4): {string.Join(", ", take4)}");  // 1,2,3,4
        Console.WriteLine($"Count: {take4.Count()}");  // 4
        
        // Skip 2 Take 3 (หน้าที่ 2 ขนาด 3)
        var page2 = numbers.Skip(3).Take(3);
        Console.WriteLine($"Skip(3).Take(3): {string.Join(", ", page2)}");  // 4,5,6
    }
}

Skip/Take กับ Database (EF Core / LINQ to SQL)

แปลงเป็น SQL OFFSET ... ROWS FETCH NEXT ... ROWS ONLY (SQL Server 2012+) หรือ LIMIT ... OFFSET ... (PostgreSQL/MySQL)


public async Task> GetPagedAsync(int pageNumber, int pageSize)
{
    var query = _context.Products.AsQueryable();
    
    var totalCount = await query.CountAsync();
    
    var items = await query
        .OrderBy(p => p.Id)
        .Skip((pageNumber - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();
    
    return new PagedResult
    {
        Items = items,
        TotalCount = totalCount,
        PageNumber = pageNumber,
        PageSize = pageSize
    };
}

TakeLast และ SkipLast (.NET 6+)


using System.Linq;

var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// TakeLast: เอาสุดท้าย n ตัว
var last3 = numbers.TakeLast(3);  // 8, 9, 10

// SkipLast: ข้ามสุดท้าย n ตัว
var skipLast3 = numbers.SkipLast(3);  // 1, 2, 3, 4, 5, 6, 7

Chunk (.NET 6+) - แบ่งเป็น Chunk/Yield


// แบ่งเป็นกลุ่มๆ ละ 3
var chunks = numbers.Chunk(3);
// [[1,2,3], [4,5,6], [7,8,9], [10]]

foreach (var chunk in chunks)
{
    Console.WriteLine($"Chunk: {string.Join(", ", chunk)}");
}

Performance Considerations

Scenarioคำแนะนำ
In-Memory (List/Array)Skip/Take เร็ว (O(1) สำหรับ List/Array)
IEnumerable (Streaming)Skip ต้อง Iterate ผ่านทุกตัว (O(n))
Database (EF Core)แปลงเป็น SQL OFFSET/FETCH - รวดเร็ว แต่ OFFSET มากช้า
Large Offset Pagingใช้ Keyset Paging (WHERE Id > @lastId) แทน Skip

Keyset Paging (Cursor-based) - เร็วกว่า Skip/Take สำหรับหน้าลึก


// แทน Skip(10000).Take(20) ที่ช้า
var nextPage = await _context.Products
    .Where(p => p.Id > lastSeenId)  // Cursor
    .OrderBy(p => p.Id)
    .Take(20)
    .ToListAsync();

สรุป

  • Skip(n).Take(m) = หน้าที่ (n/m)+1 ขนาด m
  • ลำดับสำคัญ: Skip ก่อน Take หลัง
  • ใช้ทำ Pagination ได้ง่าย
  • Database: แปลงเป็น SQL OFFSET/FETCH
  • .NET 6+: มี TakeLast, SkipLast, Chunk
  • Deep Paging: ใช้ Keyset Paging (Cursor) แทน Skip