9

更新:这是一个类似的问题


假设我有一个里面DataTable有几千个DataRows

我想将表分解成小行进行处理。

我认为 C#3 改进的数据处理能力可能会有所帮助。

这是我到目前为止的骨架:

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

关于应该替换什么的任何建议ChunkifyTableIntoSmallerChunksSomehow

我真的很感兴趣有人会如何通过访问 C#3 工具来做到这一点。如果试图应用这些工具是不合适的,请解释!


更新 3(修改了分块,因为我真的想要表,而不是 ienumerables;使用扩展方法——感谢 Jacob):

最终实现:

处理分块的扩展方法:

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

该扩展方法的示例使用者,带有来自临时测试的示例输出:

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

5 回答 5

7

这是非常可读的,并且只遍历序列一次,也许可以为您节省重复冗余Skip()/Take()调用的相当糟糕的性能特征:

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;
}
于 2009-04-20T18:41:57.930 回答
5

这似乎是 Linq 的 Skip 和 Take 方法的理想用例,具体取决于您希望通过分块实现的目标。这是完全未经测试的,从未在 IDE 代码中输入过,但您的方法可能看起来像这样。

private List<List<DataRow>> ChunkifyTable(DataTable table, int chunkSize)
{
    List<List<DataRow>> chunks = new List<List<DataRow>>();
    for (int i = 0; i < table.Rows.Count / chunkSize; i++)
    {
        chunks.Add(table.Rows.Skip(i * chunkSize).Take(chunkSize).ToList());
    }
    
    return chunks;
}
于 2009-04-20T18:12:36.113 回答
0

这是一种可能有效的方法:

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));
    }
}
于 2009-09-26T17:31:05.683 回答
0

雅各布写道

这似乎是 Linq 的 Skip 和 Take 方法的理想用例,具体取决于您希望通过分块实现的目标。这是完全未经测试的,从未在 IDE 代码中输入过,但您的方法可能看起来像这样。

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

感谢这个 Jacob - 对我有用,但我认为您示例中的测试应该是 <= 而不是 <。如果您使用 < 并且行数小于chunkSize则永远不会进入循环。类似地,最后的部分块没有被捕获,只有完整的块。正如你所说,这个例子是未经测试的,等等,所以这只是一个仅供参考,以防其他人逐字使用你的代码;-)

于 2010-04-28T23:40:35.940 回答
0

这是一种完全不同的方法。没有为块分配内存。

public static IEnumerable<IEnumerable<DataRow>> Chunkify(
    this DataTable dataTable, int chunkSize)
{
    for (int i = 0; i < dataTable.Rows.Count; i += chunkSize)
    {
        yield return GetChunk(i, Math.Min(i + chunkSize, dataTable.Rows.Count));
    }
    IEnumerable<DataRow> GetChunk(int from, int toExclusive)
    {
        for (int j = from; j < toExclusive; j++)
        {
            yield return dataTable.Rows[j];
        }
    }
}

使用示例:

var dataTable = GetTonsOfData();
foreach (var chunk in dataTable.Chunkify(1000))
{
    Console.WriteLine($"Processing chunk of {chunk.Count()} rows");
    foreach (var dataRow in chunk)
    {
        Console.WriteLine(dataRow[0]);
    }
}
于 2019-07-25T22:30:54.847 回答