2011-10-06 64 views
1

需要将一堆来自可能具有重复键的对象的键/值对添加到字典中。只有第一个不同的键的实例(和实例的值)应该被添加到字典中。如何添加到字典时,可能有重复的键?

下面是一个示例实现,首先显示工作正常。

void Main() 
{ 
    Dictionary<long, DateTime> items = new Dictionary<long, DateTime>(); 
    items = AllItems.Select(item => 
        { 
         long value; 
         bool parseSuccess = long.TryParse(item.Key, out value); 
         return new { value = value, parseSuccess, item.Value }; 
        }) 
        .Where(parsed => parsed.parseSuccess && !items.ContainsKey(parsed.value)) 
        .Select(parsed => new { parsed.value, parsed.Value }) 
        .Distinct() 
        .ToDictionary(e => e.value, e => e.Value); 
    Console.WriteLine(string.Format("Distinct: {0}{1}Non-distinct: {2}",items.Count, Environment.NewLine, AllItems.Count)); 

} 

public List<KeyValuePair<string, DateTime>> AllItems 
{ 
    get 
    { 
     List<KeyValuePair<string, DateTime>> toReturn = new List<KeyValuePair<string, DateTime>>(); 
     for (int i = 1000; i < 1100; i++) 
     { 
      toReturn.Add(new KeyValuePair<string, DateTime>(i.ToString(), DateTime.Now)); 
      toReturn.Add(new KeyValuePair<string, DateTime>(i.ToString(), DateTime.Now)); 
     } 
     return toReturn; 
    } 
} 

如果AllItems被修改,但是返回更多的对,然后一个ArgumentException出现:“具有相同键的项已被添加。”

void Main() 
{ 
    Dictionary<long, DateTime> items = new Dictionary<long, DateTime>(); 
    var AllItems = PartOne.Union(PartTwo); 
    Console.WriteLine("Total items: " + AllItems.Count()); 
    items = AllItems.Select(item => 
        { 
         long value; 
         bool parseSuccess = long.TryParse(item.Key, out value); 
         return new { value = value, parseSuccess, item.Value }; 
        }) 
        .Where(parsed => parsed.parseSuccess && !items.ContainsKey(parsed.value)) 
        .Select(parsed => new { parsed.value, parsed.Value }) 
        .Distinct() 
        .ToDictionary(e => e.value, e => e.Value); 
    Console.WriteLine("Distinct: {0}{1}Non-distinct: {2}",items.Count, Environment.NewLine, AllItems.Count()); 

} 

public IEnumerable<KeyValuePair<string, DateTime>> PartOne 
{ 
    get 
    { 
     for (int i = 10000000; i < 11000000; i++) 
     { 
      yield return (new KeyValuePair<string, DateTime>(i.ToString(), DateTime.Now)); 
     } 
    } 
} 
public IEnumerable<KeyValuePair<string, DateTime>> PartTwo 
{ 
    get 
    { 
     for (int i = 10000000; i < 11000000; i++) 
     { 
      yield return (new KeyValuePair<string, DateTime>(i.ToString(), DateTime.Now)); 
     } 
    } 
} 

完成此操作的最佳方法是什么?请注意,解决方案中需要使用long.TryParse,因为实际输入可能不包含有效的Int64。

+0

看看LINQ ToLookup()方法 – sll

+0

字典有ContainsKey函数。也许你可以在添加keyvaluepair – atbebtg

回答

5

只有第一个不同的密钥实例(和实例的值) 应该添加到字典中。

您可以通过使用Enumerable.GroupBy方法,以第一个值组中实现这一点:

items = AllItems.Select(item => 
       { 
        long value; 
        bool parseSuccess = long.TryParse(item.Key, out value); 
        return new { Key = value, parseSuccess, item.Value }; 
       }) 
       .Where(parsed => parsed.parseSuccess) 
       .GroupBy(o => o.Key) 
       .ToDictionary(e => e.Key, e => e.First().Value) 
4

我是这样看的清洗几件事情了。使用Func<string, long?>在LINQ查询中更好。

Func<string, long?> tryParse = t => 
{ 
    long v; 
    if (!long.TryParse(t, out v)) 
    { 
     return null; 
    } 
    return v; 
}; 

然后查询看起来是这样的:

var query = 
    from item in AllItems 
    let keyValue = tryParse(item.Key) 
    where keyValue.HasValue 
    group item.Value by keyValue.Value into g 
    select new 
    { 
     key = g.Key, 
     value = g.First(), 
    }; 

最后创建字典:

var items = query.ToDictionary(x => x.key, x => x.value); 

相当简单。

感谢您提供测试解决方案所需的所有代码。

+0

+1之前修改你的allItems并调用ContainsKey,以获得正确的“使用分组”答案。感谢有关如何清理的好建议。这确实使linq查询看起来更好。 – schellack

1

让我们来看看 - 你Select()目前预测到匿名类型

new { value = value, parseSuccess, item.Value }; 

然后你过滤掉其中解析失败的所有项目,所以基本上你有

new { value = value, true, item.Value }; 

现在你在使用Distinct()剩余物品。在这种情况下,(价值,价值)的所有独特组合都被认为是唯一的。这意味着你可以有(1,2)和(1,3)。

最后你创建你的字典 - 但你仍然可能有重复的value键,如上面的例子所示。这解释了为什么你会得到这个例外。

已发布GroupBy()是在这种情况下去简化你的表达。

+0

无可否认,.Distinct()并没有真正做什么,除非有多个具有相同值的键。但是,'.Where(parsed => parsed.parseSuccess &&!items.ContainsKey(parsed.value))'这一行旨在排除重复项。如果您多次运行示例,它似乎有时会排除重复项。或者那只是一条红鲱鱼? – schellack

1

我还没有尝试过这个,但是像这样的一个组应该工作。

items = AllItems.Select(item => 
       {       
        long value;       
        bool parseSuccess = long.TryParse(item.Key, out value);       
        return new { value = value, parseSuccess, item.Value };      
       })      
       .Where(parsed => parsed.parseSuccess && !items.ContainsKey(parsed.value))      
       .Select(parsed => new { parsed.value, parsed.Value })      
       .GroupBy(x => x.value) 
       .Select(x => new {value = x.Key, Value = x.Min(y => y.Value)}) 
       .ToDictionary(e => e.value, e => e.Value);