2009-04-20 23 views
7

Cập nhật: Dưới đây là một similar questionCách sạch sẽ để chia nhỏ DataTable thành các khối có kích thước cố định bằng LINQ là gì?


Giả sử tôi có một DataTable với một vài ngàn DataRows trong đó.

Tôi muốn chia bảng thành nhiều hàng nhỏ hơn để xử lý.

Tôi nghĩ khả năng làm việc với dữ liệu của C# 3 có thể hữu ích.

Đây là bộ xương tôi có cho đến nay:

DataTable Table = GetTonsOfData(); 

// Chunks should be any IEnumerable<Chunk> type 
var Chunks = ChunkifyTableIntoSmallerChunksSomehow; // ** help here! ** 

foreach(var Chunk in Chunks) 
{ 
    // Chunk should be any IEnumerable<DataRow> type 
    ProcessChunk(Chunk); 
} 

Mọi góp ý về những gì nên thay thế ChunkifyTableIntoSmallerChunksSomehow?

Tôi thực sự quan tâm đến cách một người nào đó sẽ thực hiện việc này bằng các công cụ truy cập C# 3. Nếu cố gắng áp dụng những công cụ này là không phù hợp, hãy giải thích!


Cập nhật 3 (sửa đổi chunking như tôi thực sự muốn bảng, không IEnumerables; đi với một phương pháp khuyến nông - nhờ Jacob):

thi cuối cùng:

phương pháp mở rộng để xử lý các chunking:

public static class HarenExtensions 
{ 
    public static IEnumerable<DataTable> Chunkify(this DataTable table, int chunkSize) 
    { 
     for (int i = 0; i < table.Rows.Count; i += chunkSize) 
     { 
      DataTable Chunk = table.Clone(); 

      foreach (DataRow Row in table.Select().Skip(i).Take(chunkSize)) 
      { 
       Chunk.ImportRow(Row); 
      } 

      yield return Chunk; 
     } 
    } 
} 

Ví dụ người tiêu dùng rằng phương pháp khuyến nông, với sản lượng mẫu từ một thử nghiệm quảng cáo hoc:

class Program 
{ 
    static void Main(string[] args) 
    { 
     DataTable Table = GetTonsOfData(); 

     foreach (DataTable Chunk in Table.Chunkify(100)) 
     { 
      Console.WriteLine("{0} - {1}", Chunk.Rows[0][0], Chunk.Rows[Chunk.Rows.Count - 1][0]); 
     } 

     Console.ReadLine(); 
    } 

    static DataTable GetTonsOfData() 
    { 
     DataTable Table = new DataTable(); 
     Table.Columns.Add(new DataColumn()); 

     for (int i = 0; i < 1000; i++) 
     { 
      DataRow Row = Table.NewRow(); 
      Row[0] = i; 

      Table.Rows.Add(Row); 
     } 

     return Table; 
    } 
} 
+0

Tôi không sử dụng để phân trang nhưng tôi vừa nhận ra ứng dụng song song. Nếu bạn tìm thấy bất kỳ bản sao tốt nào áp dụng ở đây, vui lòng cho tôi biết và tôi sẽ đóng câu hỏi. –

+0

Bạn có thể nội tuyến bằng cách tạo phương thức tiện ích mở rộng thực hiện ở trên. Sau đó, bạn có thể sử dụng var Chunks = từ đoạn trong bảng.Chọn lại chọn đoạn; –

+0

Tôi thích ý tưởng đó, cảm ơn –

Trả lời

6

Điều này có vẻ giống như một trường hợp sử dụng lý tưởng cho các phương thức Bỏ qua và Thực hiện của Linq, tùy thuộc vào những gì bạn muốn đạt được với việc chunking. Điều này là hoàn toàn chưa được kiểm tra, không bao giờ được nhập vào một mã IDE, nhưng phương pháp của bạn có thể trông giống như thế này.

private List<List<DataRow>> ChunkifyTable(DataTable table, int chunkSize) 
{ 
    List<List<DataRow>> chunks = new List<List<DaraRow>>(); 
    for (int i = 0; i < table.Rows.Count/chunkSize; i++) 
    { 
     chunks.Add(table.Rows.Skip(i * chunkSize).Take(chunkSize).ToList()); 
    } 

    return chunks; 
} 
+0

@Jacob: cảm ơn vì đề xuất này. Tôi đã thực hiện bảng trả về này và triển khai IEnumerable với từ khóa lợi nhuận. Tôi cũng biến nó thành một phương pháp mở rộng như bạn đề nghị. Hoạt động tuyệt vời! –

+0

Ngọt ngào! Rất vui được nghe. –

+0

Điều này bị hỏng cho bộ dữ liệu không chia hết cho 'chunkSize'. 1/10 = 0 khi sử dụng ints, vì vậy phần cuối cùng của bảng được cắt bỏ. – mydogisbox

6

này là khá dễ đọc và chỉ lặp qua chuỗi một lần, có lẽ tiết kiệm cho bạn những đặc tính hiệu suất khá xấu của lặp đi lặp lại không cần thiết Skip()/Take() cuộc gọi:

public IEnumerable<IEnumerable<DataRow>> Chunkify(DataTable table, int size) 
{ 
    List<DataRow> chunk = new List<DataRow>(size); 

    foreach (var row in table.Rows) 
    { 
     chunk.Add(row); 
     if (chunk.Count == size) 
     { 
      yield return chunk; 
      chunk = new List<DataRow>(size); 
     } 
    } 

    if(chunk.Any()) yield return chunk; 
} 
+0

Đây chắc chắn là cách tiếp cận dễ đọc nhất, nhưng nó buộc tạo một danh sách trong bộ nhớ cho mỗi đoạn . Đó là hợp lý cho kích thước chunk nhỏ nhưng không phải cho những người lớn. Ngoài ra, tôi sẽ xóa đoạn văn chứ không phải tạo ra một đoạn mới. Tôi nghĩ giải pháp tốt nhất có lẽ sẽ tạo ra một trình lặp có một trình lặp phụ, nhưng điều đó chắc chắn sẽ không dễ đọc. –

0

Dưới đây là một cách tiếp cận có thể làm việc:

public static class Extensions 
{ 
    public static IEnumerable<IEnumerable<T>> InPages<T>(this IEnumerable<T> enumOfT, int pageSize) 
    { 
     if (null == enumOfT) throw new ArgumentNullException("enumOfT"); 
     if (pageSize < 1) throw new ArgumentOutOfRangeException("pageSize"); 
     var enumerator = enumOfT.GetEnumerator(); 
     while (enumerator.MoveNext()) 
     { 
      yield return InPagesInternal(enumerator, pageSize); 
     } 
    } 
    private static IEnumerable<T> InPagesInternal<T>(IEnumerator<T> enumeratorOfT, int pageSize) 
    { 
     var count = 0; 
     while (true) 
     { 
      yield return enumeratorOfT.Current; 
      if (++count >= pageSize) yield break; 
      if (false == enumeratorOfT.MoveNext()) yield break; 
     } 
    } 
    public static string Join<T>(this IEnumerable<T> enumOfT, object separator) 
    { 
     var sb = new StringBuilder(); 
     if (enumOfT.Any()) 
     { 
      sb.Append(enumOfT.First()); 
      foreach (var item in enumOfT.Skip(1)) 
      { 
       sb.Append(separator).Append(item); 
      } 
     } 
     return sb.ToString(); 
    } 
} 
[TestFixture] 
public class Tests 
{ 
    [Test] 
    public void Test() 
    { 
     // Arrange 
     var ints = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
     var expected = new[] 
     { 
      new[] { 1, 2, 3 }, 
      new[] { 4, 5, 6 }, 
      new[] { 7, 8, 9 }, 
      new[] { 10  }, 
     }; 

     // Act 
     var pages = ints.InPages(3); 

     // Assert 
     var expectedString = (from x in expected select x.Join(",")).Join(" ; "); 
     var pagesString = (from x in pages select x.Join(",")).Join(" ; "); 

     Console.WriteLine("Expected : " + expectedString); 
     Console.WriteLine("Pages : " + pagesString); 

     Assert.That(pagesString, Is.EqualTo(expectedString)); 
    } 
} 
0

Jacob đã viết

Điều này có vẻ giống như một trường hợp sử dụng lý tưởng cho các phương thức Bỏ qua và Lấy của LINQ, tùy thuộc vào những gì bạn muốn đạt được với đoạn chunking. Đây là hoàn toàn chưa được kiểm tra, không bao giờ được nhập trong mã số IDE , nhưng phương pháp của bạn có thể trông giống như một cái gì đó như thế này.

private List<List<DataRow>> ChunkifyTable(DataTable table, int chunkSize) 
{ 
    List<List<DataRow>> chunks = new List<List<DaraRow>>(); 
    for (int i = 0; i < table.Rows.Count/chunkSize; i++) 
    { 
     chunks.Add(table.Rows.Skip(i * chunkSize).Take(chunkSize).ToList()); 
    } 

    return chunks; 
} 

Cám ơn Jacob này - hữu ích cho tôi nhưng tôi nghĩ rằng các bài kiểm tra trong ví dụ của bạn nên được < = không <.Nếu bạn sử dụng < và số hàng ít hơn chunkSize vòng lặp không bao giờ được nhập. Tương tự đoạn cuối cùng một phần không bị bắt, chỉ toàn bộ đoạn. Như bạn đã nói, ví dụ này chưa được kiểm tra, v.v. vì vậy đây chỉ là một FYI trong trường hợp người khác sử dụng mã của bạn đúng ;-)

Các vấn đề liên quan