优化Linq

时间:2014-07-22 07:48:02

标签: c# linq

我有一个包含以下内容的文件:

Aulin:            Performance Enhancers, Combat Stabilisers
i Bootis:         Fish, Basic Medicines
Aulin:            Agricultural Medicines, Combat Stabilisers
Eranin:           Tea, Coffee
LP 98-132:        Bertrandite,Gold
Dahan:            Tantalum, Explosives
Asellus Primus:   Resonant Separators, Non Lethal Weapons
LHS 3006:         Bertrandite, Indite

这些是来自着名游戏的价值观。

现在我读取数据并使用以下代码将其转换为Dictionary

var imports = File.ReadAllText(@"d:\exports.txt");
var productsDict = 
    imports
    .Split('\n')
    .Select(line => line.Split(':'))
    .GroupBy(line => line[0])
    .ToDictionary(
        line => line.Key, 
        line => 
           line.Select(item => item[1])
           .Aggregate((c, n) => c.Insert(c.Length, "," + n))
           .Split(',')
           .Select(i => i.Trim(' '))
           .Distinct()
    );

我可以优化LINQ吗?何时必须为链接lambda使用新标识符?如您所见,我使用lineitemi等等。

3 个答案:

答案 0 :(得分:2)

最快的方法(并不是很难实现)是逐行手动解析文件,然后是char-by-char。 (如果要进行解析性能)步骤:

  1. 虽然不是EOF(文件结束)
  2. 阅读单行
  3. 读取字符,直到第一个:出现。
  4. 请记住只读字作为键
  5. 虽然不是EOL(行尾):读取字符直到逗号,然后将单词放在列表中,返回到5)
  6. 放置字典条目(其中键是第一个读取的单词,值是从步骤5获得的单词列表)
  7. 返回1)
  8. 如果在您的情况下性能不是主键(文件不是很大),您可以将一些LINQ方法分组为单独的方法(例如扩展方法),如:

    public static IEnumerable<string> SplitByLine(this string text)
    {
       return text.Split('\n');
    }
    
    public  static IEnumerable<string[]> KeyValuesSplitted(this IEnumerable<string> lines)
    {
       return lines.Select(line => line.Split(':'));
    }
    
    public static IEnumerable<IGrouping<string[]>> GroupyByKey(this IEnumerable<string[]> keyValuesSplitted)
    {
       return keyValuesSplitted.GroupBy(line => line.First());
    }
    
    // and so on..
    

    用法:

    productsDict = imports.SplitByLine()
                          .KeyValuesSplitted()
                          .GroupyByKey() //and so on.
    

    在这种情况下,每种方法都很容易理解,我们知道导入时会发生什么。

答案 1 :(得分:0)

您可以引入一些局部变量以使事物更具可读性。 使事物看起来更好,更易读的另一个技巧是将链中的每个函数调用放在一个新行上。这样,您可以从上到下读取代码,而不是向右滚动。

你应该将它包装在一个名为ConvertData()或ImportData()之类的方法中,或者甚至在单独的类中。这样你的代码看起来也更加可重复,因为你不必知道导入是如何完成的,并且你不会被复杂的代码分散注意力。

您可以这样做:

var imports = File.ReadAllText(@"d:\exports.txt");
var productsDict = importData(imports);

然后在一个单独的方法中,或者在一个单独的类中更好(除非这个类只负责导入数据):

private  Dictionary<string, IEnumerable<string>> importData(string imports)
    {
      return imports.Split('\n')
            .Select(line => line.Split(':'))
            .GroupBy(line => line[0])
            .ToDictionary(line => line.Key,
                          line => line.Select(item => item[1])
                                      .Aggregate((c, n) => c.Insert(c.Length, ","+n))
                                      .Split(',')
                                      .Select(i => i.Trim(' '))
                                      .Distinct()
                         );
    }

更好,但可能超出问题范围,是创建一个IImporter接口,在界面中定义了导入方法,以便以后您可以随时切换Importer类,或支持多个Importers,而不会破坏其他部分你的代码

答案 2 :(得分:0)

首先,不是将整个文件加载到内存中,而是每次都可以读取每一行。

var lines = File.ReadLines(@"d:\exports.txt");

然后,您在框架中遗漏了许多可以使用的工具(例如string.JoinSelectMany),以及更简单的合成语:

var productDict = (from line in lines
                   let keyValue = line.Split(':')
                   let lineValues = from value in keyValue[1].Split(',')
                                    select value.Trim()
                   group lineValues by keyValue[0] into entry
                   select new
                   {
                       entry.Key,
                       Entry = (from values in entry
                                from value in values
                                select value).Distinct(),
                   })
                   .ToDictionary(
                       entry => entry.Key,
                       entry => string.Join(",", entry.Entry));