Ответ 1
Здесь очевидное, не linq решение:
foreach(var person in personList)
{
if(!myDictionary.Keys.Contains(person.FirstAndLastName))
myDictionary.Add(person.FirstAndLastName, person);
}
У меня есть список объектов Person. Я хочу преобразовать в словарь, где ключ - это имя и фамилия (конкатенация), а значение - объект Person.
Проблема в том, что у меня есть дублированные люди, поэтому это взрывается, если я использую этот код:
private Dictionary<string, Person> _people = new Dictionary<string, Person>();
_people = personList.ToDictionary(
e => e.FirstandLastName,
StringComparer.OrdinalIgnoreCase);
Я знаю, это звучит странно, но на данный момент меня не интересуют имена дубликатов. Если есть несколько имен, я просто хочу их захватить. В любом случае, я могу написать этот код выше, поэтому он просто берет одно из имен и не взорвется на дубликаты?
Здесь очевидное, не linq решение:
foreach(var person in personList)
{
if(!myDictionary.Keys.Contains(person.FirstAndLastName))
myDictionary.Add(person.FirstAndLastName, person);
}
Решение LINQ:
// Use the first value in group
var _people = personList
.GroupBy(p => p.FirstandLastName, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
// Use the last value in group
var _people = personList
.GroupBy(p => p.FirstandLastName, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.Last(), StringComparer.OrdinalIgnoreCase);
Если вы предпочитаете решение, отличное от LINQ, вы можете сделать что-то вроде этого:
// Use the first value in list
var _people = new Dictionary<string, Person>(StringComparer.OrdinalIgnoreCase);
foreach (var p in personList)
{
if (!_people.ContainsKey(p.FirstandLastName))
_people[p.FirstandLastName] = p;
}
// Use the last value in list
var _people = new Dictionary<string, Person>(StringComparer.OrdinalIgnoreCase);
foreach (var p in personList)
{
_people[p.FirstandLastName] = p;
}
Linq-решение с использованием Distinct() и без группировки:
var _people = personList
.Select(item => new { Key = item.Key, FirstAndLastName = item.FirstAndLastName })
.Distinct()
.ToDictionary(item => item.Key, item => item.FirstFirstAndLastName, StringComparer.OrdinalIgnoreCase);
Я не знаю, лучше ли это решение LukeH, но оно также работает.
Это должно работать с выражением лямбда:
personList.Distinct().ToDictionary(i => i.FirstandLastName, i => i);
Чтобы справиться с устранением дубликатов, используйте IEqualityComparer<Person>
, который можно использовать в методе Distinct()
, а затем получить ваш словарь будет легко.
Дано:
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.FirstAndLastName.Equals(y.FirstAndLastName, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(Person obj)
{
return obj.FirstAndLastName.ToUpper().GetHashCode();
}
}
class Person
{
public string FirstAndLastName { get; set; }
}
Получить словарь:
List<Person> people = new List<Person>()
{
new Person() { FirstAndLastName = "Bob Sanders" },
new Person() { FirstAndLastName = "Bob Sanders" },
new Person() { FirstAndLastName = "Jane Thomas" }
};
Dictionary<string, Person> dictionary =
people.Distinct(new PersonComparer()).ToDictionary(p => p.FirstAndLastName, p => p);
Вы также можете использовать функцию ToLookup
LINQ, которую вы можете использовать почти взаимозаменяемо с помощью словаря.
_people = personList
.ToLookup(e => e.FirstandLastName, StringComparer.OrdinalIgnoreCase);
_people.ToDictionary(kl => kl.Key, kl => kl.First()); // Potentially unnecessary
Это будет по существу делать GroupBy в ответ LukeH, но даст хеширование, предоставляемое Словарем. Таким образом, вам, вероятно, не нужно преобразовывать его в словарь, но просто используйте функцию LINQ First
всякий раз, когда вам нужно получить доступ к значению для ключа.
Вы можете создать метод расширения, подобный ToDictionary(), с той разницей, что он позволяет дублировать. Что-то вроде:
public static Dictionary<TKey, TElement> SafeToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
IEqualityComparer<TKey> comparer = null)
{
var dictionary = new Dictionary<TKey, TElement>(comparer);
if (source == null)
{
return dictionary;
}
foreach (TSource element in source)
{
dictionary[keySelector(element)] = elementSelector(element);
}
return dictionary;
}
В этом случае, если есть дубликаты, то выигрывает последнее значение.
DataTable DT = new DataTable();
DT.Columns.Add("first", typeof(string));
DT.Columns.Add("second", typeof(string));
DT.Rows.Add("ss", "test1");
DT.Rows.Add("sss", "test2");
DT.Rows.Add("sys", "test3");
DT.Rows.Add("ss", "test4");
DT.Rows.Add("ss", "test5");
DT.Rows.Add("sts", "test6");
var dr = DT.AsEnumerable().GroupBy(S => S.Field<string>("first")).Select(S => S.First()).
Select(S => new KeyValuePair<string, string>(S.Field<string>("first"), S.Field<string>("second"))).
ToDictionary(S => S.Key, T => T.Value);
foreach (var item in dr)
{
Console.WriteLine(item.Key + "-" + item.Value);
}
Начиная с решения Carra вы также можете записать его как:
foreach(var person in personList.Where(el => !myDictionary.ContainsKey(el.FirstAndLastName)))
{
myDictionary.Add(person.FirstAndLastName, person);
}