Home Articles

整合和过滤iList项目,查找paterns,实践或现有方法

Asked
Viewed 1945 times
0

我正在将二进制文件读入(t)的绑定列表中;绑定到datagridview .
文件中的每一行代表一个事务,但我需要合并和/或过滤符合特定条件的事务 .

我从机械的角度知道如何做到这一点(在添加每个项目时循环列表,添加新项目,或者将数据与现有项目合并),但我正在寻找练习,模式,现有组件或我缺少的其他东西(我正在为搜索关键字画一个空白) .

如果我没有,我不想重新发明轮子 . 我特别关注速度和性能问题,在某些情况下要处理100k以上的记录 .

目前正在使用.NET 2.0,但如果存在特别性感的解决方案,则将移至3.5 .


Update 我've changed the solution to 3.5 so that'不再是问题 . 我应该指出这个项目是VB.NET,但是我可以为这个特定的函数添加一个新的C#库来利用C#迭代器 .

2 Answers

  • 1

    是的,你想要3.5因为这给你LINQ语言集成查询 .

    性能成本很低,但对于大型记录集,您可以通过使用PLINQ(并行处理)来抵消这一点 .

    LINQ是处理集合的声明性,功能性方法 .

    您需要的概念:

    • lambda表达式 () =>
    • 扩展方法

    从一组10,000个字符串中考虑,您希望前100个长度超过24个字符:

    var result = source.Where(s => s.Length > 24).Take(100);
    

    从一组 Person 对象中,您想要返回名称,但它们分为 firstNamelastName 属性 .

    var result = source.Select(person => person.firstName + person.LastName);
    

    这将返回 IEnumerable<string> .

    从同一组你想要的平均年龄:

    var result = source.Average(person => person.Age);
    

    最年轻的10人:

    var result = source.OrderBy(person => person.Age).Take(10);
    

    每个人,按姓氏的第一个字母分组:

    var result = source.GroupBy(person => person.lastName[0]);
    

    返回 IGrouping<char, Person>

    姓氏以S开头的最老的25个人的姓名:

    var result = source.Where(person => person.lastName.StartsWith("S"))
       .OrderByDescending(person => person.Age)
       .Take(25)
       .Select(person => person.firstName + person.lastName);
    

    想象一下,你需要在 foreach 循环中编写多少代码才能实现这一目标,以及在代码中引入缺陷或错过优化的空间有多大 . LINQ语法的声明性质使其更易于阅读和维护 .

    有一种替代语法是SQL-ish,但显示了如何真正定义针对任意对象集的查询 . 考虑一下你想得到名字叫“Bob”的人:

    var result = 
        from person in source
        where person.firstName == "Bob"
        select person;
    

    它看起来很奇怪,但如果你从2.0跳起来,这是有效的C#代码 .

    我唯一的警告是,一旦你使用LINQ,你可能会拒绝再次使用2.0 .

    有很多很好的资源可用于学习LINQ语法 - 它不需要很长时间 .


    更新

    针对第1条评论的其他注意事项:

    您已经拥有了一个非常强大的工具,可以使用C#2.0 - 迭代器 .

    考虑:

    public class Foo
    {
        private IEnumerable<Record> GetRecords()
        {
            Record record = // do I/O stuff, instantiate a record
    
            yield return record;
        }
    
        public void DisplayRecords()
        {
            foreach (Record record in GetRecords())
            {
                // do something meaningful
    
                // display the record
            }
        }
    }
    

    那么,这有什么值得注意的呢? GetRecords() 方法是一个迭代器块, yield 关键字按请求返回结果("lazy evaluation") .

    这意味着当你调用 DisplayRecords() 时,它会调用 GetRecords() ,但只要 GetRecords() 有一个 Record ,它就会将它返回到 DisplayRecords() ,然后可以用它做一些有用的事情 . 当 foreach 块再次循环时,执行将返回 GetRecords() ,这将返回下一个项目,依此类推 .

    这样,您就不必等待从磁盘读取100,000条记录,然后才能开始排序和显示结果 .

    这给了一些有趣的可能性;是否可以在您的情况下使用它取决于您(例如,您不希望刷新网格绑定100,000次) .

  • 0

    听起来你想在伪LINQ中做这样的事情: data.GroupBy().Select(Count()).Where() - 按一些标准分组(合并),计算每组中的数字,然后按结果过滤 .

    但是,您建议您可能有太多数据一次性加载到内存中,因此您希望在加载数据时进行整合 . 这可以使用您自己的 GroupByCount 运算符完成,有点像这个过度简化的版本:

    public static IEnumerable<KeyValuePair<T, int>>
        GroupByCount<T>(IEnumerable<T> input)
    {
        Dictionary<T, int> counts = new Dictionary<T, int>();
        foreach (T item in input)
            if (counts.ContainsKey(item))
                counts[item]++;
            else
                counts[item] = 1;
        return counts;
    }
    

    那么你只需要 data.GroupByCount().Where() 并且你的数据在加载时都会被合并,因为 foreach 只会在处理完前一个项目后加载下一个项目 .

Related