挑战:优雅LINQify这个程序代码

时间:2008-10-26 21:32:48

标签: linq linq-to-objects

string[] filesOfType1 = GetFileList1();
string[] filesOfType2 = GetFileList2();
var cookieMap = new Dictionary<string, CookieContainer>();

Action<string, Func<string, KeyValuePair<string, CookieContainer>>> addToMap = (filename, pairGetter) =>
    {
        KeyValuePair<string, CookieContainer> cookiePair;
        try
        {
            cookiePair = pairGetter(filename);
        }
        catch
        {
            Console.WriteLine("An error was encountered while trying to read " + file + ".");
            return;
        }

        if (cookieMap.ContainsKey(cookiePair.Key))
        {
            if (cookiePair.Value.Count > cookieMap[cookiePair.Key].Count)
            {
                cookieMap[cookiePair.Key] = cookiePair.Value;
            }
        }
        else
        {
            cookieMap.Add(cookiePair.Key, cookiePair.Value);
        }
    };


foreach (string file in filesOfType1)
{
    addToMap(file, GetType1FileCookiePair);
}
foreach (string file in filesOfType2)
{
    addToMap(file, GetType2FileCookiePair);
}

必须保留的显着特征:

  • 类型1的文件比类型2的文件更重要;即如果类型1的文件映射到(key,value1)组合而类型2的文件映射到(key,value2)组合,那么我们将(key,value1)添加到cookieMap并且不是(键,值2)。 编辑:正如Bevan所指出的那样,我的原始程序代码并不满足。
  • 其次,CookieContainer具有更高Count的{​​{1}}具有更高的优先级,即如果同一个键有两个(键,值)组合,并且都来自同一文件类型,我们选择一个更高value.Count
  • 每个案例的异常处理是必须的;拧紧单个文件阅读应该只允许我们注意并继续。

我最好的尝试就像这样:

var cookieMap = (filesOfType1.Select(file => GetType1FileCookiePair(file))
                .Concat(filesOfType2.Select(file => GetType2FileCookiePair(file))))
                    .GroupBy(pair => pair.Key)
                    .Select(/* some way of selecting per the above bullets */)
                    .ToDictionary(pair => pair.Key, pair => pair.Value);

但是它不够优雅并且填补了评论块看起来像个婊子。现在我很高兴保持程序性,但我认为看看人们是否能想出一些非常聪明的东西可能是一个有趣的挑战。

3 个答案:

答案 0 :(得分:3)

这是我的尝试 - 将任务分成三个不同的陈述似乎最简单。

我正在使用一个辅助函数,如果该操作抛出异常,则返回null - 为了与Omer van Kloeten的答案保持一致,我称之为Swallow()

另外,我没有使用LINQ语法,只是System.Linq.Enumerable提供的扩展方法

最后,请注意,这是未编译的 - 因此请将其视为意图。

// Handle all files of type 1
var pairsOfType1 = 
    filesOfType1
        .Select( file => Swallow( pairGetter(file)))
        .Where( pair => pair != null);

// Handle files of type 2 and filter out those with keys already provided by type 1
var pairsOfType2 =
    filesOfType2
        .Select( file => Swallow( pairGetter(file)))
        .Where( pair => pair != null);
        .Where( pair => !pairsOfType1.Contains(p => p.Key == pair.Key));

// Merge the two sets, keeping only the pairs with the highest count
var cookies =
    pairsOfType1
        .Union( pairsOfType2)
        .GroupBy( pair => pair.Key)
        .Select( group => group.OrderBy( pair => pair.Value.Count).Last());
        .ToDictionary( pair => pair.Key);

答案 1 :(得分:2)

using CookiePair = KeyValuePair<string, CookieContainer>;
using CookieDictionary = Dictionary<string, CookieContainer>;

Func<string[], Func<string, CookiePair>, IEnumerable<CookiePair>> getCookies =
  ( files, pairGetter ) =>
    files.SelectMany( filename => {
      try { return new[] { pairGetter( filename ) }; }
      catch { Console.WriteLine( "..." ); return new CookiePair[0]; }
    } );

var type1Cookies = getCookies( filesOfType1, GetType1FileCookiePair ).ToArray( );
var type1CookieNames = type1Cookies.Select( p => p.Key ).ToArray( );
var type2Cookies = getCookies( filesOfType2, GetType2FileCookiePair )
  .Where( p => !type1CookieNames.Contains( p.Key ) );

var cookieMap = type1Cookies.Concat( type2Cookies )
  .Aggregate( new CookieDictionary( ), ( d, p ) => {
    if( !d.ContainsKey( p.Key ) || p.Value.Count > d[p.Key].Count )
      d[p.Key] = p.Value;
    return d;
  } );

编辑:更新了Cookie检索,以满足“类型1的文件比类型2的文件更重要”的要求。

答案 2 :(得分:1)

请原谅我实际上并没有继续编译,但这就是我要去做的方式:

var cookieMap = (from pair in
                     (from f1 in filesOfType1
                      select Swallow(() => GetType1FileCookiePair(f1)))
                         .Concat(from f2 in filesOfType2
                                 select Swallow(() => GetType2FileCookiePair(f2)))
                         .SelectMany(dict => dict)
                 group pair by pair.Key into g
                 select g)
                .ToDictionary(g => g.Key, g => g.Select(pair => pair.Value)
                                                .OrderByDescending(value => value.Count)
                                                .First());

Swallow如下:

private static T Swallow<T>(Func<T> getT)
{
    try { return getT(); } catch { }

    return default(T);
}

爱我一个好的LINQ。

  • 修改:添加了Swallow方法,可以吞下所有例外情况。
  • 编辑2:已编译,已更改等。已添加Swallow。现在按预期工作。