如果可能有重复键,如何添加到字典?

时间:2011-10-06 15:33:22

标签: c# linq dictionary

需要将来自可能具有重复键的对象的一组键/值对添加到字典中。只应将键的第一个不同实例(以及实例的值)添加到字典中。

下面是一个示例实现,首先看起来可以正常工作。

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。

4 个答案:

答案 0 :(得分: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)

答案 1 :(得分:4)

我会看清楚一些事情。

在LINQ查询中使用Func<string, long?>会更好。

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);

相当简单。

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

答案 2 :(得分:1)

让我们看看 - 您的Select()目前正在投放到匿名类型

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

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

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

现在您对其余项目使用Distinct()。在这种情况下,(value,Value)的所有唯一组合都被认为是唯一的。这意味着你可以有(1,2)和(1,3)。

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

已发布GroupBy()是本案例中简化表达的方法。

答案 3 :(得分: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);