Объединение блоков строк
Есть список списков строк.
List<string> firstList = new List<string>(){"one","two,"three"};
List<string> secondList = new List<string>(){"three","four,"five"};
List<string> thirdList = new List<string>(){"ten","eleven,"twelve"};
List<List<string>> blocksOfStrings = new List<List<string>>(){firstList,secondList,thirdList};
И нужно написать метод, который объединит листы с пересекающимися значениями. Т.е. в данном случае у нас firstList и secondList содержат одинаковый string - "three", значит их нужно объединить в один List с удалением дубликатов
Написал для этого метод,
List<List<string>> MergeBlockOfStrings(List<List<string>> blocksOfStrings)
{
List<List<string>> newBlockOfStrings = new List<List<string>>();
foreach (var block in blocksOfStrings)
{
foreach (var block2 in blocksOfStrings)
{
if (block2 != block)
{
foreach (var str1 in block)
{
foreach (var str2 in block2)
{
if (str1 == str2)
{
var newBlock = new List<string>();
foreach (var firstBlockString in block)
{
if (!newBlock.Contains(firstBlockString))
{
newBlock.Add(firstBlockString);
}
}
foreach (var secondBlockString in block2)
{
if (!newBlock.Contains(secondBlockString))
{
newBlock.Add(secondBlockString);
}
}
newBlockOfStrings.Add(newBlock);
}
}
}
}
}
}
return newBlockOfStrings;
}
но он громоздкий и выглядит ужасно. Можно как-то это сделать проще и понятнее, например через LINQ?
Как должно работать
На входе в метод
List<string> firstList = new List<string>(){"one","two","three"};
List<string> secondList = new List<string>(){"three","four","five"};
List<string> thirdList = new List<string>(){"ten","eleven","twelve"};
Использование метода
List<List<string>> MergedBlocksOfStrings = MergeBlockOfStrings(blockOfStrings);
На выходе должны быть
List<string> mergedList = new List<string>(){"one","two","three","four","five"};
List<string> thirdList = new List<string>(){"ten","eleven","twelve"};
List<List<string>> blocksOfStrings = new List<List<string>>(){mergedList,thirdList};
Ответы (2 шт):
Буду рад, если кто-то напишет в комментарии об эффективности этого алгоритма и его подводных камнях.
Идея
- Берем самый левый блок
left - Проверяем поочередно с правой частью списка блоков, есть ли там блок
right, элементы которого присутствуют в спискеleft. Если есть - см. шаг 3, если нет, см. шаг 7 - Объединяем списки в один
join - Удаляем
leftиrightиз исходного списка блоков - Вставляем на место
leftновый блокjoin - Завершаем цикл по правой части списка
- Если блок
left- не последний в списке, то переходим ко следующему по списку блокуleftи повторяем шаг 2. Иначе - завершение алгоритма
По итогу в исходном списке будет находиться ожидаемый результат.
Код:
private static async Task Main(string[] args)
{
List<string> firstList = new List<string>() { "one", "two", "three" };
List<string> secondList = new List<string>() { "three", "four", "five" };
List<string> thirdList = new List<string>() { "ten", "eleven", "twelve" };
List<List<string>> blocksOfStrings = new List<List<string>>() { firstList, secondList, thirdList};
var result = Intersect(blocksOfStrings);
}
private static List<List<string>> Intersect(List<List<string>> blocksOfStrings)
{
for (var i = 0; i < blocksOfStrings.Count; i++)
{
var left = blocksOfStrings[i];
for (var j = i + 1; j < blocksOfStrings.Count; j++)
{
var right = blocksOfStrings[j];
if (AreIntersect(left, right))
{
var join = left.Union(right).ToList();
blocksOfStrings.Remove(left);
blocksOfStrings.Remove(right);
blocksOfStrings.Insert(i, join);
i--;
break;
}
}
}
return blocksOfStrings;
}
private static bool AreIntersect(List<string> first, List<string> second)
{
return first.Intersect(second).Any();
}
Для объединения можно использовать Concat, для дублей Union,
List<string> firstList = new List<string>() { "one", "two", "three"};
List<string> secondList = new List<string>() { "three", "four", "five"};
List<string> thirdList = new List<string>() { "ten", "eleven", "twelve"};
List<string> result = firstList.Union(secondList).Union(thirdList).ToList();
Если List содержит класс то можно еще объеденять через общий интерфейс, например:
firstList.Cast<ICommon>().Concat(secondList.Cast<ICommon>()).Concat(thirdList.Cast<ICommon>());