• 解决Linq.ToDictionary()时的键重复问题


    今天在使用 Linq 的 ToDictionary() 时发生了异常,提示:

    System.ArgumentException: 已添加了具有相同键

    因为以前一直没有遇到有相同键的情况,所以从来没关注过这个问题。然后写段试验来码来处理这个问题

    问题再现

    csclass Program
    {
    	public static void Main(string[] args)
    	{
    		var data = new[]{
    			Tuple.Create("001", "James"),
    			Tuple.Create("002", "Linda"),
    			Tuple.Create("003", "Frank"),
    			Tuple.Create("004", "Jack"),
    			Tuple.Create("002", "Rose"),
    			Tuple.Create("001", "Lynn"),
    			Tuple.Create("008", "Luke")
    		};
    		var dict = data.ToDictionary(t => t.Item1, t => t.Item2);
    		// 这里就抛异常了
    		// System.ArgumentException: 已添加了具有相同键的
    		foreach (var pair in dict)
    		{
    			// 不要见怪,用了 C# 6.0 的语法
    			Console.WriteLine($"{pair.Key} = {pair.Value}");
    		}
       }
    }
    
    

    使用 ToLookup() 解决

    原来 ToDictionary() 不会处理重复键,也没有提供多的参数来处理重复键。想了一下,这种问题大概应该用 ToLookup() 来解决,所以改写了代码

    cs        // var dict = data.ToDictionary(t => t.Item1, t => t.Item2);
            // System.ArgumentException: 已添加了具有相同键的
    
            var dict = data.ToLookup(t => t.Item1, t => t.Item2)
                .ToDictionary(t => t.Key, t => t.First());
    

    ToLookup() 返回的是一个 ILookup<> 接口,看定义是一个 IGrouping<> 接口的枚举。 IGrouping<> 本身也是一个 IEnumerable<> ,具有 Key 属性。因为 IGrouping<> 将具有同一个键的所有元素都包含在内了,所以在生成 Dictionary 的时候只取其中一个元素,根据上下文需要,可以取 First() ,或者 Last() 。

    这是 ILookup<> 和 IGrouping<> 的定义

    cspublic interface ILookup<TKey, TElement>
        : IEnumerable<IGrouping<TKey, TElement>>, IEnumerable
        
    public interface IGrouping<out TKey, out TElement>
        : IEnumerable<TElement>, IEnumerable
    

    自定义 ToDictionaryEx() 来解决

    因为 ToLookup() 返回的结果包含太多枚举对象,所以不知道性能如何。如果不放心,可以自己写个 ToDictionaryEx() 来解决,当然也不敢保证性能就比 ToLookup() 好,要实验了才知道。

    csstatic class ToDictionaryExtentions
    {
    	public static IDictionary<TKey, TValue> ToDictionaryEx<TElement, TKey, TValue>(
    		this IEnumerable<TElement> source,
    		Func<TElement, TKey> keyGetter,
    		Func<TElement, TValue> valueGetter)
    	{
    		IDictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
    		foreach (var e in source)
    		{
    			var key = keyGetter(e);
    			if (dict.ContainsKey(key))
    			{
    				continue;
    			}
    			dict.Add(key, valueGetter(e));
    		}
    		return dict;
    	}
    }
    
    

    现在实验代码又该改改了

    cs        // var dict = data.ToDictionary(t => t.Item1, t => t.Item2);
            // System.ArgumentException: 已添加了具有相同键的
    
            //var dict = data.ToLookup(t => t.Item1, t => t.Item2)
            //    .ToDictionary(t => t.Key, t => t.First());
    
            var dict = data.ToDictionaryEx(t => t.Item1, t => t.Item2);
    

    性能

    没有测试性能的问题,等暴露了性能问题再来测试吧

  • 相关阅读:
    GitHub挂载网站
    JS中用execCommand("SaveAs")保存页面兼容性问题解决方案
    使用List,Dictionary加载数据库中的数据
    模拟在table中移动鼠标,高亮显示鼠标所在行
    ASP.NET AutoCompleteType 属性,这么多年的IT人你发现了吗?
    生成流水号
    在数据库中查找字符串(不知道表名的情况下 查找字符串)
    半角与全角之间的转换
    C#代码规范精简表
    有关Excel导出
  • 原文地址:https://www.cnblogs.com/sjqq/p/6890454.html
Copyright © 2020-2023  润新知