Как добавить в словарь, когда могут быть дубликаты ключей? - PullRequest
1 голос
/ 06 октября 2011

Связка пар ключ / значение из объекта, который может иметь повторяющиеся ключи, должна быть добавлена ​​в словарь. Только первый отдельный экземпляр ключа (и значение экземпляра) должен быть добавлен в словарь.

Ниже приведен пример реализации, которая вначале работает нормально.

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 ]

5 голосов
/ 06 октября 2011

Только первый отдельный экземпляр ключа (и значение экземпляра) следует добавить в словарь.

Этого можно добиться с помощью метода 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 голосов
/ 06 октября 2011

Я бы посмотрел, как очистить несколько вещей.

Использование 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);

Довольно просто.

Спасибо за предоставление всего кода, необходимого для тестирования решения.

1 голос
/ 06 октября 2011

Я еще не пробовал, но что-то вроде этого с группой должно работать.

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); 
1 голос
/ 06 октября 2011

Давайте посмотрим - ваш Select() в настоящее время проецируется на анонимный тип

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

Затем вы отфильтровываете все элементы, где не удалось выполнить синтаксический анализ, поэтому, по сути, у вас есть

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

СейчасВы используете Distinct() на остальных предметах.В этом случае все уникальные комбинации (значение, значение) считаются уникальными.Это означает, что у вас может быть то есть (1,2) и (1,3).

Наконец, вы создаете свой словарь - но у вас все еще могут быть дубликаты value ключей, как показано в примере выше.Это объясняет, почему вы получаете это исключение.

Как уже упоминалось, GroupBy() - это способ в этом случае упростить ваше выражение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...