Есть признаки документа и введенные вручную примечания к документу. Признак документа содержит текстовое описание, которое может пересекаться с примечанием. Хочется (и требуется по ТЗ) печатать что-то одно. В идеале - то, что длиннее, но для начала - хотя бы чтобы остался только один.
Пример: в комментарии введено "поставить печать", в признаке - "Поставить печать!!!"
Описан следующий компарер:
internal class StringIncludeComparer:IEqualityComparer
public int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
Собрав в итоге комментарии из документов и комментарии из признаков документов в один List<{ID, Comment}> делаю следующее:
from trp in tmpPostReport
group trp.Comment by trp.Id
into CommPers
select new { Id= CommPers.Key, Comment = string.Join("; ",
CommPers.Distinct(new StringIncludeComparer()).ToArray()) };
Но в итоге получаю строку "поставить печать; Поставить печать!!!"
Вопрос:
Что не так с компарером?
Как сделать так, чтобы при сравнении строк "поставить печать" и "Поставить печать!!!" выбиралась всегда большая по длине, т.е. "Поставить печать!!!"
Ответ
Ваша проблема в том, что GetHashCode не согласован с Equals. так делать нельзя. Должно выполняться условие: если Equals возвращает true, то и хэшкоды должны быть равны.
Например, вы можете вернуть 0 в GetHashCode
Для того, чтобы выбрать самую короткую строку, можно попробовать использовать groupby:
CommPers.GroupBy(s => s, new StringIncludeComparer())
.Select(g => g.OrderBy(s => s.Length).First());
Но учтите, что для EqualityComparer'а ваше отношение равенства должно быть транзитивно, как это отмечено в другом ответе. Поэтому код имеет право и не сработать.
Для того, чтобы ваше сравнение гарантировано работало несмотря на то, что оно нетранзитивно, применим тяжёлую артиллерию. Откажемся от IEqualityComparer'а (так как мы всё равно не можем удовлетворить его инвариант), и сделаем группировку, которая вычисляет транзитивное замыкание вашего равенства: если Equals(a, b) и Equals(b, c) оба равны true, то мы считаем элементы a и c равными вне зависимости от Equals(a, c) (и так далее). Заметьте, что нам теперь придётся сравнивать (почти) каждый элемент с каждым, так что производительность пострадает.
static class EnumerableExtensions
{
class TransitiveGrouping
public static IEnumerable
Теперь ваш запрос должен работать так:
CommPers.TransitiveGroupBy(s => s, new StringIncludeComparer().Equals)
.Select(g => g.OrderByDescending(s => s.Length).First());
Вот рабочий пример: http://ideone.com/OT2GqM
Я сильно не отлаживал, так что возможны баги. Если что, сообщайте, пофиксим.
Комментариев нет:
Отправить комментарий