Я думаю, что чистое решение LINQ будет чрезмерно сложным. Это не так (если я правильно понимаю вашу спецификацию) - простая операция объединения. Вы хотите объединение на основе непустых пересечений. Это означало бы сначала переупорядочить данные, чтобы LINQ мог выполнить соединение, чтобы найти данные, которые соответствуют, и поскольку LINQ будет только присоединяться к равенству, выполнение этого при сохранении исходной информации группировки приведет к синтаксису, который будет больше проблема, чем это стоит, ИМХО.
Вот подход, не LINQ, который работает для примера вы дали:
static void Main(string[] args)
{
char[][] letters =
{
new [] { 'A', 'B' },
new [] { 'C', 'D' },
new [] { 'B', 'A', 'F' },
new [] { 'I', 'F', 'J' },
};
List<HashSet<char>> sets = new List<HashSet<char>>();
foreach (char[] row in letters)
{
List<int> setIndexes = Enumerable.Range(0, sets.Count)
.Where(i => row.Any(ch => sets[i].Contains(ch))).ToList();
CoalesceSets(sets, row, setIndexes);
}
foreach (HashSet<char> set in sets)
{
Console.WriteLine("{ " + string.Join(", ", set) + " }");
}
}
private static void CoalesceSets(List<HashSet<char>> sets, char[] row, List<int> setIndexes)
{
if (setIndexes.Count == 0)
{
sets.Add(new HashSet<char>(row));
}
else
{
HashSet<char> targetSet = sets[setIndexes[0]];
targetSet.UnionWith(row);
for (int i = setIndexes.Count - 1; i >= 1; i--)
{
targetSet.UnionWith(sets[setIndexes[i]]);
sets.RemoveAt(setIndexes[i]);
}
}
}
Это создает наборы входных данных путем сканирования ранее идентифицированных наборов, чтобы найти те, которые текущая строка данные пересекаются с, а затем объединяют эти множества в один набор, содержащий все члены (ваша спецификация, похоже, налагает транзитивное членство и hellip, т.е. если одна буква соединяет множества A и B, а другая буква объединяет множество B и C, вы хотите, чтобы A , B и C все объединены в один набор).
Это не оптимальное решение, но оно читаемо. Вы могли бы избежать поиска O (N^2), поддерживая Dictionary<char, int>
, чтобы отобразить каждый символ в набор, который содержит его. Затем вместо сканирования всех наборов это простой поиск каждого символа в текущей строке, чтобы создать список установленных индексов. Но есть гораздо больше «домашнего» кода, который подходит к этому подходу; Я бы не стал ее реализовывать таким образом, если вы не найдете доказанной проблемы с производительностью, сделав ее более простым способом.
К слову: у меня есть смутное воспоминание. Я видел этот тип вопроса до переполнения стека, т. Е. Такого рода транзитивное объединение множеств. Я искал вопрос, но не мог его найти. Возможно, вам повезет больше, и вы найдете дополнительную полезную информацию с этим вопросом и его ответами.
Чтобы понять вашу проблему, чтобы придумать решение, у меня есть пара вопросов. Откуда берутся ценности? В ваших результирующих данных, почему «C» и «D» находятся в другой группе от остальных? – cChacon
@cЧакон значения исходят из моего набора данных, но он может использовать любые значения. В общем, мне нужно группировать по сходству элемента в массиве с другим элементом в другом массиве. «C» и «D» не группируются, потому что в «Письмах» нет элементов [] [] содержит «C» и «D». Итак, сначала группы A/B и B/A/F вместе, в результате получим 'A/B/F'. Затем он группировался с 'I/F/J', в результате получился' A/B/F/I/J'. В группе нет двойных/дублированных элементов. И 'C/D' - единственный массив, который не имеет одинаковой буквы с другими элементами массива (буква), поэтому он группируется в другом. –
Тогда мое предложение - использовать linq для группировки этих данных из вашего набора данных. Используя linq, вы можете сгруппировать свой набор данных И вернуть ему список строк в одном выражении. Здесь вы можете найти множество примеров на этом сайте.Код в основном будет выглядеть так: lstOfStrings = (от g в dsChars группа по x выберите x) .ToList(); – cChacon