using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TransitiveGroupBy
{
static class EnumerableExtensions
{
class TransitiveGrouping<K, T> : List<T>, IGrouping<K, T>
{
public TransitiveGrouping(K key) { Key = key; }
public K Key { get; private set; }
internal List<K> EqualKeys = new List<K>();
}
public static IEnumerable<IGrouping<K, T>> TransitiveGroupBy<T, K>(
this IEnumerable<T> sequence,
Func<T, K> keySelector,
Func<K, K, bool> keyComparer)
{
var result = new List<TransitiveGrouping<K, T>>();
foreach (T curr in sequence)
{
K currKey = keySelector(curr);
var containingGroups = result
.Where(tg => tg.EqualKeys.Any(kk => keyComparer(kk, currKey)))
.ToList();
if (containingGroups.Count == 0)
{
// add a new group
var newGroup = new TransitiveGrouping<K, T>(currKey);
newGroup.Add(curr);
newGroup.EqualKeys.Add(currKey);
result.Add(newGroup);
}
else
{
var targetGroup = containingGroups.First();
targetGroup.Add(curr);
// merge the groups (transitive closure)
foreach (var group in containingGroups.Skip(1))
{
targetGroup.AddRange(group);
targetGroup.EqualKeys.AddRange(group.EqualKeys);
result.Remove(group);
}
}
}
return result;
}
}
internal static class StringIncludeComparer
{
public static bool Equals(string x, string y)
{
if (x.ToUpper().Contains(y.ToUpper())) { return true; }
if (y.ToUpper().Contains(x.ToUpper())) { return true; }
return false;
}
}
class Program
{
static void Main(string[] args)
{
var values = new List<string>
{
"поставить печать",
"не ставить печать",
"Поставить печать!!!",
"Поставить печать??"
};
var values2 = new List<string>
{
"Поставить печать!!!",
"не ставить печать",
"Поставить печать??",
"поставить печать"
};
foreach (var s in values2.TransitiveGroupBy(s => s, StringIncludeComparer.Equals)
.Select(g => g.OrderByDescending(s => s.Length).First()))
Console.WriteLine(s);
foreach (var s in values2.TransitiveGroupBy(s => s, StringIncludeComparer.Equals)
.Select(g => g.OrderByDescending(s => s.Length).First()))
Console.WriteLine(s);
}
}
}
dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLkxpbnE7CnVzaW5nIFN5c3RlbS5UZXh0Owp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwoKbmFtZXNwYWNlIFRyYW5zaXRpdmVHcm91cEJ5CnsKICAgIHN0YXRpYyBjbGFzcyBFbnVtZXJhYmxlRXh0ZW5zaW9ucwogICAgewogICAgICAgIGNsYXNzIFRyYW5zaXRpdmVHcm91cGluZzxLLCBUPiA6IExpc3Q8VD4sIElHcm91cGluZzxLLCBUPgogICAgICAgIHsKICAgICAgICAgICAgcHVibGljIFRyYW5zaXRpdmVHcm91cGluZyhLIGtleSkgeyBLZXkgPSBrZXk7IH0KICAgICAgICAgICAgcHVibGljIEsgS2V5IHsgZ2V0OyBwcml2YXRlIHNldDsgfQogICAgICAgICAgICBpbnRlcm5hbCBMaXN0PEs+IEVxdWFsS2V5cyA9IG5ldyBMaXN0PEs+KCk7CiAgICAgICAgfQoKICAgICAgICBwdWJsaWMgc3RhdGljIElFbnVtZXJhYmxlPElHcm91cGluZzxLLCBUPj4gVHJhbnNpdGl2ZUdyb3VwQnk8VCwgSz4oCiAgICAgICAgICAgICAgICB0aGlzIElFbnVtZXJhYmxlPFQ+IHNlcXVlbmNlLAogICAgICAgICAgICAgICAgRnVuYzxULCBLPiBrZXlTZWxlY3RvciwKICAgICAgICAgICAgICAgIEZ1bmM8SywgSywgYm9vbD4ga2V5Q29tcGFyZXIpCiAgICAgICAgewogICAgICAgICAgICB2YXIgcmVzdWx0ID0gbmV3IExpc3Q8VHJhbnNpdGl2ZUdyb3VwaW5nPEssIFQ+PigpOwogICAgICAgICAgICBmb3JlYWNoIChUIGN1cnIgaW4gc2VxdWVuY2UpCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEsgY3VycktleSA9IGtleVNlbGVjdG9yKGN1cnIpOwogICAgICAgICAgICAgICAgdmFyIGNvbnRhaW5pbmdHcm91cHMgPSByZXN1bHQKICAgICAgICAgICAgICAgICAgICAgICAgLldoZXJlKHRnID0+IHRnLkVxdWFsS2V5cy5Bbnkoa2sgPT4ga2V5Q29tcGFyZXIoa2ssIGN1cnJLZXkpKSkKICAgICAgICAgICAgICAgICAgICAgICAgLlRvTGlzdCgpOwogICAgICAgICAgICAgICAgaWYgKGNvbnRhaW5pbmdHcm91cHMuQ291bnQgPT0gMCkKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAvLyBhZGQgYSBuZXcgZ3JvdXAKICAgICAgICAgICAgICAgICAgICB2YXIgbmV3R3JvdXAgPSBuZXcgVHJhbnNpdGl2ZUdyb3VwaW5nPEssIFQ+KGN1cnJLZXkpOwogICAgICAgICAgICAgICAgICAgIG5ld0dyb3VwLkFkZChjdXJyKTsKICAgICAgICAgICAgICAgICAgICBuZXdHcm91cC5FcXVhbEtleXMuQWRkKGN1cnJLZXkpOwogICAgICAgICAgICAgICAgICAgIHJlc3VsdC5BZGQobmV3R3JvdXApOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHZhciB0YXJnZXRHcm91cCA9IGNvbnRhaW5pbmdHcm91cHMuRmlyc3QoKTsKICAgICAgICAgICAgICAgICAgICB0YXJnZXRHcm91cC5BZGQoY3Vycik7CiAgICAgICAgICAgICAgICAgICAgLy8gbWVyZ2UgdGhlIGdyb3VwcyAodHJhbnNpdGl2ZSBjbG9zdXJlKQogICAgICAgICAgICAgICAgICAgIGZvcmVhY2ggKHZhciBncm91cCBpbiBjb250YWluaW5nR3JvdXBzLlNraXAoMSkpCiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXRHcm91cC5BZGRSYW5nZShncm91cCk7CiAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldEdyb3VwLkVxdWFsS2V5cy5BZGRSYW5nZShncm91cC5FcXVhbEtleXMpOwogICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQuUmVtb3ZlKGdyb3VwKTsKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDsKICAgICAgICB9CiAgICB9CgogICAgaW50ZXJuYWwgc3RhdGljIGNsYXNzIFN0cmluZ0luY2x1ZGVDb21wYXJlcgogICAgewogICAgICAgIHB1YmxpYyBzdGF0aWMgYm9vbCBFcXVhbHMoc3RyaW5nIHgsIHN0cmluZyB5KQogICAgICAgIHsKICAgICAgICAgICAgaWYgKHguVG9VcHBlcigpLkNvbnRhaW5zKHkuVG9VcHBlcigpKSkgeyByZXR1cm4gdHJ1ZTsgfQogICAgICAgICAgICBpZiAoeS5Ub1VwcGVyKCkuQ29udGFpbnMoeC5Ub1VwcGVyKCkpKSB7IHJldHVybiB0cnVlOyB9CiAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICB9CiAgICB9CgogICAgY2xhc3MgUHJvZ3JhbQogICAgewogICAgICAgIHN0YXRpYyB2b2lkIE1haW4oc3RyaW5nW10gYXJncykKICAgICAgICB7CiAgICAgICAgICAgIHZhciB2YWx1ZXMgPSBuZXcgTGlzdDxzdHJpbmc+IAogICAgICAgICAgICB7IAogICAgICAgICAgICAgICAgItC/0L7RgdGC0LDQstC40YLRjCDQv9C10YfQsNGC0YwiLAogICAgICAgICAgICAgICAgItC90LUg0YHRgtCw0LLQuNGC0Ywg0L/QtdGH0LDRgtGMIiwKICAgICAgICAgICAgICAgICLQn9C+0YHRgtCw0LLQuNGC0Ywg0L/QtdGH0LDRgtGMISEhIiwKICAgICAgICAgICAgICAgICLQn9C+0YHRgtCw0LLQuNGC0Ywg0L/QtdGH0LDRgtGMPz8iCiAgICAgICAgICAgIH07CgoKICAgICAgICAgICAgdmFyIHZhbHVlczIgPSBuZXcgTGlzdDxzdHJpbmc+IAogICAgICAgICAgICB7IAogICAgICAgICAgICAgICAgItCf0L7RgdGC0LDQstC40YLRjCDQv9C10YfQsNGC0YwhISEiLAogICAgICAgICAgICAgICAgItC90LUg0YHRgtCw0LLQuNGC0Ywg0L/QtdGH0LDRgtGMIiwKICAgICAgICAgICAgICAgICLQn9C+0YHRgtCw0LLQuNGC0Ywg0L/QtdGH0LDRgtGMPz8iLAogICAgICAgICAgICAgICAgItC/0L7RgdGC0LDQstC40YLRjCDQv9C10YfQsNGC0YwiCiAgICAgICAgICAgIH07CgogICAgICAgICAgICBmb3JlYWNoICh2YXIgcyBpbiB2YWx1ZXMyLlRyYW5zaXRpdmVHcm91cEJ5KHMgPT4gcywgU3RyaW5nSW5jbHVkZUNvbXBhcmVyLkVxdWFscykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5TZWxlY3QoZyA9PiBnLk9yZGVyQnlEZXNjZW5kaW5nKHMgPT4gcy5MZW5ndGgpLkZpcnN0KCkpKQogICAgICAgICAgICAgICAgQ29uc29sZS5Xcml0ZUxpbmUocyk7CgogICAgICAgICAgICBmb3JlYWNoICh2YXIgcyBpbiB2YWx1ZXMyLlRyYW5zaXRpdmVHcm91cEJ5KHMgPT4gcywgU3RyaW5nSW5jbHVkZUNvbXBhcmVyLkVxdWFscykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5TZWxlY3QoZyA9PiBnLk9yZGVyQnlEZXNjZW5kaW5nKHMgPT4gcy5MZW5ndGgpLkZpcnN0KCkpKQogICAgICAgICAgICAgICAgQ29uc29sZS5Xcml0ZUxpbmUocyk7CiAgICAgICAgfQogICAgfQp9Cg==