2026-06-05 15:58:03 +07:00
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
|
|
|
|
|
|
namespace QWERTYkez.ExcelProcessor;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Множество строк, которое автоматически приводит все добавляемые элементы
|
|
|
|
|
|
/// к верхнему регистру и удаляет диакритические знаки (например, 'ё' -> 'Е').
|
|
|
|
|
|
/// Реализует ISet<string>, поэтому может использоваться там, где ожидается этот интерфейс.
|
|
|
|
|
|
/// </summary>
|
2026-06-08 14:31:31 +07:00
|
|
|
|
internal class NormalizedSet : ISet<string>
|
2026-06-05 15:58:03 +07:00
|
|
|
|
{
|
|
|
|
|
|
private readonly HashSet<string> _inner;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Создаёт пустое нормализованное множество.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public NormalizedSet()
|
|
|
|
|
|
{
|
|
|
|
|
|
_inner = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Создаёт нормализованное множество, заполненное элементами из указанной коллекции.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="collection">Коллекция, элементы которой будут нормализованы и добавлены.</param>
|
|
|
|
|
|
public NormalizedSet(IEnumerable<string> collection)
|
|
|
|
|
|
{
|
|
|
|
|
|
_inner = [.. collection.Select(Normalize)];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Нормализует строку: верхний регистр и удаление диакритики.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static string Normalize(string s)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(s))
|
|
|
|
|
|
return s;
|
|
|
|
|
|
|
|
|
|
|
|
var normalized = s.Normalize(NormalizationForm.FormD);
|
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
|
foreach (char c in normalized)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
|
|
|
|
|
|
sb.Append(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
return sb.ToString().Normalize(NormalizationForm.FormC).ToUpperInvariant();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- Реализация ISet<string> ----------
|
|
|
|
|
|
|
|
|
|
|
|
public bool Add(string item) => _inner.Add(Normalize(item));
|
|
|
|
|
|
|
|
|
|
|
|
void ICollection<string>.Add(string item) => Add(item);
|
|
|
|
|
|
|
|
|
|
|
|
public void UnionWith(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var item in other)
|
|
|
|
|
|
Add(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void IntersectWith(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
_inner.IntersectWith(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void ExceptWith(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
_inner.ExceptWith(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void SymmetricExceptWith(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
_inner.SymmetricExceptWith(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsSubsetOf(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
return _inner.IsSubsetOf(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsSupersetOf(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
return _inner.IsSupersetOf(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsProperSupersetOf(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
return _inner.IsProperSupersetOf(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsProperSubsetOf(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
return _inner.IsProperSubsetOf(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool Overlaps(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
return _inner.Overlaps(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool SetEquals(IEnumerable<string> other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var normalizedOther = new HashSet<string>(other.Select(Normalize));
|
|
|
|
|
|
return _inner.SetEquals(normalizedOther);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- Реализация ICollection<string> ----------
|
|
|
|
|
|
|
|
|
|
|
|
public int Count => _inner.Count;
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsReadOnly => false;
|
|
|
|
|
|
|
|
|
|
|
|
public void Clear() => _inner.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
public bool Contains(string item) => _inner.Contains(Normalize(item));
|
|
|
|
|
|
|
|
|
|
|
|
public void CopyTo(string[] array, int arrayIndex) => _inner.CopyTo(array, arrayIndex);
|
|
|
|
|
|
|
|
|
|
|
|
public bool Remove(string item) => _inner.Remove(Normalize(item));
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- Реализация IEnumerable<string> и IEnumerable ----------
|
|
|
|
|
|
|
|
|
|
|
|
public IEnumerator<string> GetEnumerator() => _inner.GetEnumerator();
|
|
|
|
|
|
|
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
|
|
|
|
}
|