گاهي از اوقات نياز ميشود تا در يك ليست، آيتمهاي تكراري موجود را مشخص كرد. به صورت پيش فرض متد Distinct براي حذف مقادير تكراري در يك ليست با استفاده از LINQ موجود است كه البته آنهم اما و اگرهايي دارد كه در ادامه به آن پرداخته خواهد شد، اما باز هم اين مورد پاسخ سؤال اصلي نيست (نميخواهيم موارد تكراري را حذف كنيم).
براي حذف آيتمهاي تكراري از يك ليست جنريك ميتوان متد زير را نوشت:
public static List<T> RemoveDuplicates<T>(List<T> items)
{
return (from s in items select s).Distinct().ToList();
}
public static void TestRemoveDuplicates()
{
List<string> sampleList =
new List<string>() { "A1", "A2", "A3", "A1", "A2", "A3" };
sampleList = RemoveDuplicates(sampleList);
foreach (var item in sampleList)
Console.WriteLine(item);
}
public class Employee
{
public int ID { get; set; }
public string FName { get; set; }
public int Age { get; set; }
}
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};
lstEmp = RemoveDuplicates<Employee>(lstEmp);
foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
براي رفع اين مشكل بايد از آرگومان دوم متد distinct جهت معرفي وهلهاي از كلاسي كه اينترفيس IEqualityComparer را پياده سازي ميكند، كمك گرفت.
public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);
public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
//آيا دقيقا يك وهله هستند؟
if (Object.ReferenceEquals(x, y)) return true;
//آيا يكي از وهلهها نال است؟
if (Object.ReferenceEquals(x, null) ||
Object.ReferenceEquals(y, null))
return false;
return x.Age == y.Age && x.FName == y.FName && x.ID == y.ID;
}
public int GetHashCode(Employee obj)
{
if (Object.ReferenceEquals(obj, null)) return 0;
int hashTextual = obj.FName == null ? 0 : obj.FName.GetHashCode();
int hashDigital = obj.Age.GetHashCode();
return hashTextual ^ hashDigital;
}
}
public static List<T> RemoveDuplicates<T>(List<T> items, IEqualityComparer<T> comparer)
{
return (from s in items select s).Distinct(comparer).ToList();
}
public static void TestRemoveDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};
lstEmp = RemoveDuplicates(lstEmp, new EmployeeComparer());
foreach (var item in lstEmp)
Console.WriteLine(item.FName);
}
سؤال: براي يافتن آيتمهاي تكراري يك ليست چه بايد كرد؟
احتمالا مقاله "روشهايي براي حذف ركوردهاي تكراري" را به خاطر داريد. اينجا هم ميتوان كوئري LINQ ايي را نوشت كه ركوردها را بر اساس سن، گروه بندي كرده و سپس گروههايي را كه بيش از يك ركورد دارند، انتخاب نمايد.
public static void FindDuplicates()
{
List<Employee> lstEmp = new List<Employee>()
{
new Employee(){ ID=1, Age=20, FName="F1"},
new Employee(){ ID=2, Age=21, FName="F2"},
new Employee(){ ID=1, Age=20, FName="F1"},
};
var query = from c in lstEmp
group c by c.Age into g
where g.Count() > 1
select new { Age = g.Key, Count = g.Count() };
foreach (var item in query)
{
Console.WriteLine("Age {0} has {1} records", item.Age, item.Count);
}
}